# KPK Docs

A comprehensive source of information about KPK's vault and funds products, automations, risk management, and infrastructure

<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask anything about KPK</button>

<button type="button" class="button secondary" data-action="ask" data-query="What is KPK?">What is KPK?</button><button type="button" class="button secondary" data-action="ask" data-query="How do KPK&#x27;s vaults and funds differentiate?">Compare KPK's vaults and funds</button>

<h2 align="center"> What are you looking for?</h2>

<p align="center">Whether you're a user researching KPK's product suite, a partner assessing KPK's automation, or a builder integrating KPK strategies, we get you covered.</p>

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-type="content-ref"></th><th data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-type="image">Cover image (dark)</th><th data-hidden data-type="image">Cover image (dark)</th><th data-hidden data-type="image">Cover image (dark)</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><h4><i class="fa-user-vneck" style="color:$primary;">:user-vneck:</i></h4></td><td><h4><strong>Users</strong></h4></td><td>Learn more about products specs and features</td><td><a href="https://docs.kpk.io/vaults/">Vaults</a></td><td><a href="https://docs.kpk.io/funds/">Funds</a></td><td><a href="https://gitbook-templates.gitbook.io/product-docs/">Documentation</a></td><td></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FIyewPOOYiImsIXPP1PBa%2FVaults_D.png?alt=media&amp;token=5693d5e7-bf35-4c68-8065-5a26401fb7b0">Vaults_D.png</a></td><td></td><td></td><td></td></tr><tr><td><i class="fa-people-group">:people-group:</i></td><td><h4>Partners</h4></td><td>Understand how your assets can be onboarded</td><td><a href="/vaults/infrastructure/risk-framework">Risk framework</a></td><td><a href="/funds/infrastructure/onchain-accounting">Onchain Accounting</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td><h4><i class="fa-terminal" style="color:$primary;">:terminal:</i></h4></td><td><h4><strong>Integrators</strong></h4></td><td>Browse, test, and implement APIs</td><td><a href="/vaults/integration/integration">Integration</a></td><td><a href="/funds/integration/subscription-request">Integration</a></td><td><a href="https://gitbook-templates.gitbook.io/api-reference/">API Reference</a></td><td></td><td></td><td></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FIyewPOOYiImsIXPP1PBa%2FVaults_D.png?alt=media&amp;token=5693d5e7-bf35-4c68-8065-5a26401fb7b0">Vaults_D.png</a></td><td></td></tr></tbody></table>

***

## Strategies

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><h4><strong>Vaults</strong></h4></td><td>Automated lending strategies tiered by different risk profiles across the major DeFi lending protocols</td><td><a href="https://docs.kpk.io/vaults/">Vaults</a></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FmGKHxsVddO1BqsLtMQEt%2FVaults_L.png?alt=media&amp;token=9d88fe86-8f84-4003-8365-9894e39bbbce">Vaults_L.png</a></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FmGKHxsVddO1BqsLtMQEt%2FVaults_L.png?alt=media&amp;token=9d88fe86-8f84-4003-8365-9894e39bbbce">Vaults_L.png</a></td></tr><tr><td><h4>Funds</h4></td><td>Tokenised, actively managed portfolios with per-block onchain accounting and composable shares </td><td><a href="https://docs.kpk.io/funds/">Funds</a></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FfXnXkXPu3cQkxBjgXQ64%2FFunds_L.png?alt=media&amp;token=6e08b745-5a8c-432f-ad6f-d9ad52cefb16">Funds_L.png</a></td><td><a href="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2F6W1lJkFVLumPBNU4tuI3%2FFunds_D.png?alt=media&amp;token=b499b22e-1fba-4fe5-b8fb-e824f4690daf">Funds_D.png</a></td></tr></tbody></table>

***

&#x20;

{% columns %}
{% column width="33.33333333333333%" %}

<figure><img src="https://2172973029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTBKvv5dkTfvKc0yNPDyg%2Fuploads%2FnYTW8ZzMqyTHNpUbZrSv%2FKPKBlog.png?alt=media&amp;token=755681d2-2228-4f18-84f1-554e2d462fe8" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="66.66666666666667%" valign="middle" %}

## Learn more about the KPK developments

Read articles, reports, and case studies on how KPK is advancing non custodial asset management onchain

<a href="https://kpk.io/blog" class="button primary" data-icon="book-open">Blog</a> <a href="https://x.com/kpk_io" class="button secondary" data-icon="x-twitter"></a><a href="https://www.linkedin.com/company/kpk-io/" class="button primary" data-icon="linkedin"></a>
{% endcolumn %}
{% endcolumns %}


# Introduction

This documentation is the canonical reference for **KPK-curated products across supported protocols**. It explains the strategy design, asset selection, incentives, fees, and risk controls that govern each product.

**Who this is for:** Depositors, integrators, and risk reviewers who need precise, up-to-date information about KPK-curated products.

KPK-curated products combine a clear risk framework, deep protocol integration, in-house automation, and a network of issuer and protocol partners to deliver stable and scalable strategies.

### Approach

KPK-curated products prioritise robust risk management and stability while using advanced in-house automation to enhance efficiency and returns. The goal is to deliver **superior risk-adjusted yields** compared to other curators, without increasing exposure.

Each curated product benefits from:

{% columns %}
{% column %}
**Continuous monitoring**\
with clear response procedures.
{% endcolumn %}

{% column %}
**Thorough due diligence**\
for every integrated asset and protocol.
{% endcolumn %}
{% endcolumns %}

{% columns %}
{% column %}
**Risk-tiered allocation**\
assigning each whitelisted strategy a tier that determines its allocation limits.
{% endcolumn %}

{% column %}
**Conservative strategy design**\
targeting sustainable yield from reputable sources.
{% endcolumn %}
{% endcolumns %}

{% columns %}
{% column %}
**Automated agents**\
that trigger protective actions in response to risk alerts and, where applicable, optimise allocations to enhance yield
{% endcolumn %}

{% column %}
**Aligned incentives**\
including temporary reward campaigns that complement sustainable yield, never replace it.
{% endcolumn %}
{% endcolumns %}

### Explore

This handbook is organised by protocol.

Each section provides an overview of the relevant protocol and KPK’s strategy in the context of that protocol’s design and constraints. A page is then provided for each KPK-curated product ([example](/vaults/vaults/gearbox/ethereum/eth)), which contains strategy details, parameters, integrated assets, limitations, risk notes, and a change log.

<table data-card-size="large" data-view="cards"><thead><tr><th align="center"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td align="center"><strong>Morpho Vaults</strong></td><td><a href="/vaults/vaults/morpho">Morpho</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FMIqcrNNtLDVRiUFy3e5G%2FMorpho_L.png?alt=media&amp;token=58341344-6625-4972-a864-36a3a1ec205a">Morpho_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdqdWFE1qo3TqtO2THZ7S%2FMorpho_D.png?alt=media&amp;token=f1e52786-841a-4bae-926b-5b72490a5c8f">Morpho_D.png</a></td></tr><tr><td align="center"><strong>Gearbox Earn Pools</strong></td><td><a href="/vaults/vaults/gearbox">Gearbox</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FLp2dxo1JINOQgCRlw1xC%2FGearbox_L.png?alt=media&amp;token=8514dac5-07e5-4c63-9e68-fd48f2d5879e">Gearbox_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FXtwpOvJ0ry0bQUVnx5pX%2FGearbox_D.png?alt=media&amp;token=2d158910-cb0a-4ee4-ae07-1dcb93c30621">Gearbox_D.png</a></td></tr><tr><td align="center"><strong>Euler Vaults</strong></td><td><a href="/vaults/vaults/euler">Euler</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGzVGquHQLEKJOzPpDLDb%2FEuler_L.png?alt=media&amp;token=12a5e78c-6326-4039-ad24-73e3b0a68ef1">Euler_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FItH2nBKGVFLoL2lWppcs%2FEuler_D.png?alt=media&amp;token=022b29bb-f3e9-47aa-80b3-692565bd070c">Euler_D.png</a></td></tr><tr><td align="center"><strong>Symbiotic Vaults</strong></td><td><a href="/vaults/vaults/symbiotic">Symbiotic</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-0ccbdcb3124df657e7f9ceeeda42e7d8550cd5f9%2FSymbiotic_L.png?alt=media">Symbiotic_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-1c2bd206907b39e8b6e1d924de0e07d9969cf821%2FSymbiotic_D.png?alt=media">Symbiotic_D.png</a></td></tr></tbody></table>

In addition, the section on [Risk Framework](/vaults/infrastructure/risk-framework) provides overarching guidance on KPK’s approach to risk management in the context of curated products. The [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer), [Gearbox Disclaimer](/vaults/resources/legal/gearbox-disclaimer), [Euler Disclaimer](/vaults/resources/legal/euler-disclaimer), and [Symbiotic Disclaimer](/vaults/resources/legal/symbiotic-disclaimer) sections outline the terms on which the KPK-curated products are offered to users.


# Gearbox

KPK pools on Gearbox

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>WETH Earn Pool</strong></td><td>Ethereum</td><td><a href="https://docs.kpk.io/curation/gearbox/weth">https://docs.kpk.io/curation/gearbox/weth</a></td><td><a href="/vaults/vaults/gearbox/ethereum/eth">ETH</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fj4LHxYNgwRWdlymhOKTI%2FGearbox_WETH_L.png?alt=media&amp;token=36051993-1b4b-4be8-a685-0220399384c3">Gearbox_WETH_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FYjsZmFvq9Loe5JMstqfB%2FGearbox_WETH_D-1.png?alt=media&amp;token=545927fa-fed0-409c-aaae-8bf913ff7b96">Gearbox_WETH_D-1.png</a></td></tr><tr><td><strong>wstETH Earn Pool</strong></td><td>Ethereum</td><td><a href="https://docs.kpk.io/curation/gearbox/wsteth">https://docs.kpk.io/curation/gearbox/wsteth</a></td><td><a href="/vaults/vaults/gearbox/ethereum/wsteth">wstETH</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FAAlGJy5tYXHybCrfvfCM%2FGearbox_WETH_L-1.png?alt=media&amp;token=9d53b243-830e-4389-87c3-b1d6f17bb2d3">Gearbox_WETH_L-1.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FvEoUlpN735LOd5UnvGQt%2FGearbox_WETH_D.png?alt=media&amp;token=adc9df06-c5ad-4ba1-9245-4b24e0e6b1c3">Gearbox_WETH_D.png</a></td></tr></tbody></table>

### Protocol overview

[Gearbox](https://gearbox.fi/) is a modular lending environment that enables borrowers to open *credit accounts* to take leveraged positions across whitelisted collateral such as LSTs, LRTs and ERC-4626 vaults. Depositors supply liquidity to **ERC-4626 Earn pools** and earn yield; curators configure parameters and safeguards for each market.

{% hint style="success" %}
**Live metrics:** Track TVL, borrow APY, supply APY, utilisation and more for KPK Gearbox vaults on Dune: <https://dune.com/kpk/kpk-gearbox-vaults>
{% endhint %}

### Curator model

Curators set boundaries for underlying markets such as liquidation thresholds, collateral quota limits, utilisation caps, oracle choices and adapter allowlists. Unlike allocator models (e.g. Morpho), curators don’t move funds between markets. They define safe operating conditions and can pause pools in emergencies.

KPK’s role is twofold: **pool creation** and **pool monitoring.** The first sets up each pool with defined parameters and safeguards; the second keeps those settings in line with changing liquidity, utilisation, and risk conditions.<br>

1. **Pool creation and configuration**
   * Initial due diligence before collateral whitelisting.
   * [Dual-oracle pricing](https://docs.gearbox.fi/gearbox-permissionless-doc/competitive-advantages/dual-oracle-pricing) for every asset, with staleness/ownership monitors.
   * Utilisation caps to preserve an instant withdrawable liquidity buffer.
   * Quota limits (pool-wide exposure caps) per strategy/asset.
   * Direct LST/LRT withdrawals where supported (e.g. Mellow assets).
   * Min/max debt limits to bound borrower behaviour.
   * Strict adapter allowlists (AMM swaps, selected ERC-4626 vaults).
2. **Pool monitoring and fine-tuning**
   * Track borrow utilisation, liquidity depth, oracle health, wrapper status, and governance changes.
   * Balance borrow and lend sides via parameter updates (quotas, LTs, utilisation caps).
   * Protective actions on alerts: pause, tighten/disable collateral, adjust limits.
   * Material changes are recorded in each product’s change log.

For the full methodology, see the [Curation Risk Framework](/vaults/infrastructure/risk-framework).

### Vault parameters

Per-pool parameters (tiers, quotas, LTs, oracles, adapters) are documented on the product subpages and kept up to date in the [Changelog section](https://docs.kpk.io/changelog/).

* [Ethereum](/vaults/vaults/gearbox/ethereum)
  * [KPK WETH Pool](/vaults/vaults/gearbox/ethereum/eth)
  * [KPK wstETH Pool](/vaults/vaults/gearbox/ethereum/wsteth)


# Ethereum

KPK-curated Gearbox Earn Pools on Ethereum.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>WETH Earn Pool</strong></td><td>Ethereum</td><td><a href="https://docs.kpk.io/curation/gearbox/weth">https://docs.kpk.io/curation/gearbox/weth</a></td><td><a href="/vaults/vaults/gearbox/ethereum/eth">ETH</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fj4LHxYNgwRWdlymhOKTI%2FGearbox_WETH_L.png?alt=media&amp;token=36051993-1b4b-4be8-a685-0220399384c3">Gearbox_WETH_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FYjsZmFvq9Loe5JMstqfB%2FGearbox_WETH_D-1.png?alt=media&amp;token=545927fa-fed0-409c-aaae-8bf913ff7b96">Gearbox_WETH_D-1.png</a></td></tr><tr><td><strong>wstETH Earn Pool</strong></td><td>Ethereum</td><td><a href="https://docs.kpk.io/curation/gearbox/wsteth">https://docs.kpk.io/curation/gearbox/wsteth</a></td><td><a href="/vaults/vaults/gearbox/ethereum/wsteth">wstETH</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FAAlGJy5tYXHybCrfvfCM%2FGearbox_WETH_L-1.png?alt=media&amp;token=9d53b243-830e-4389-87c3-b1d6f17bb2d3">Gearbox_WETH_L-1.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FvEoUlpN735LOd5UnvGQt%2FGearbox_WETH_D.png?alt=media&amp;token=adc9df06-c5ad-4ba1-9245-4b24e0e6b1c3">Gearbox_WETH_D.png</a></td></tr></tbody></table>


# ETH

The KPK ETH Earn Pool channels deposits into Gearbox’s Credit Account system across selected collateral markets. Yield comes from overcollateralised lending rates. Strict utilisation caps, quota limits, and dual-oracle pricing help preserve **liquidity buffers** and mitigate market risks.

<mark style="color:$primary;">The vault</mark> provides exposure to **risk-adjusted ETH yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/gearbox-eth-mainnet">https://app.kpk.io/vaults/gearbox-eth-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-gearbox-vaults#kpk-weth-pool">https://dune.com/kpk/kpk-gearbox-vaults#kpk-weth-pool</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://app.euler.finance/earn/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245?network=ethereum">Euler</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>WETH</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x9396dcbf78fc526bb003665337c5e73b699571ef#code">0x9396dcbf78fc526bb003665337c5e73b699571ef</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>90%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>Utilisation target</strong></td><td>90%</td></tr></tbody></table>

### Strategy

Liquidity from this pool is lent to Gearbox borrowers through the Credit Account mechanism. Borrowers use whitelisted LSTs, LRTs, WBTC, and ERC-4626 vaults as collateral to build leveraged looping strategies, typically targeting AMM, restaking or staking yields.

The pool doesn’t allocate funds on its own. Instead, it relies on borrower activity within **pre-defined pool parameters** set by KPK. Yield comes primarily from borrower interest rates.

This pool is designed to support **high-demand collateral markets** while maintaining strict utilisation caps to preserve instant liquidity for depositors. Native minting and withdrawals are enabled for various assets within Gearbox. Borrowers can enter the native unstaking queue directly through the protocol, allowing positions to unwind gracefully. This avoids common pitfalls in lending markets without native exit support and enables smoother user onboarding, offboarding, and liquidations.

### Risk framework

The pool’s parameters follow KPK’s [Curation Risk Framework](/vaults/infrastructure/risk-framework), which governs asset due diligence, tiering, monitoring, and response procedures. All configuration and parameter updates are recorded in the [Changelog page](https://docs.kpk.io/changelog/). Entries include scope, rationale, and transaction references for full transparency.

#### Risk-tier snapshot

Each asset in a KPK Gearbox market is linked to two price feeds: a main oracle and a reserve oracle. These typically combine a market-rate oracle (e.g., Chainlink or RedStone) and a fundamental oracle (reflecting the underlying exchange or redemption rate). The main oracle is used for account valuation and liquidation checks, while the reserve oracle serves as a safety reference during collateral withdrawals or external calls. This [dual-oracle setup](https://docs.gearbox.fi/gearbox-permissionless-doc/competitive-advantages/dual-oracle-pricing) balances market accuracy with resistance to manipulation, protecting LPs during volatility events such as LST depegs.

| Collateral                                                                                       | Issuer     | Risk tier | Quota limit | Liquidation threshold | Main oracle                                                                              | Reserve oracle                                                                           |
| ------------------------------------------------------------------------------------------------ | ---------- | --------- | ----------- | --------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| wstETH                                                                                           | Lido       | A         | 3,000       | 95%                   | ​[Fundamental](https://etherscan.io/address/0x587660d2d2ccba5efd0594ccfda03c5adc1a3dc9)​ | ​[Chainlink](https://etherscan.io/address/0xd68BB36b2C3b27B4e412fe032A3eF2e406Bc97bb)​   |
| stETH                                                                                            | Lido       | A         | 3,000       | 95%                   | ​[Chainlink](https://etherscan.io/address/0x95c4f803933c01ad8f587d688a84d3a3043842ee)​   | ​[Fundamental](https://etherscan.io/address/0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419)​ |
| WBTC                                                                                             | BitGo      | B         | 500         | 87%                   | ​[Chainlink](https://etherscan.io/address/0xc185b51f8baa8b9089ecc01fd91d575763a5397b)​   | ​[RedStone](https://etherscan.io/address/0xAB7f623fb2F6fea6601D4350FA0E2290663C28Fc)​    |
| weETH                                                                                            | EtherFi    | B         | 750         | 93%                   | ​[Chainlink](https://etherscan.io/address/0x1b91b05f1b9d5c1ac665486258b4d8eae124d3ee)​   | ​[Fundamental](https://etherscan.io/address/0x9f77d36cd0816f55409f5675ee00f870acdc068f)​ |
| rETH                                                                                             | Rocketpool | B         | 750         | 93%                   | ​[Fundamental](https://etherscan.io/address/0x4c5125e6A4c8fB56DEBCAB65254f992A9268a689)​ | ​[Redstone](https://etherscan.io/address/0x5239Bc291c5F0F9b3999ea48f63F43d73669C209)​    |
| ezETH                                                                                            | Renzo      | B         | 0           | 93%                   | ​[Fundamental](https://etherscan.io/address/0x36ae97c11a2C1b19FB6bDcD2383652488C48a67F)​ | ​[Redstone](https://etherscan.io/address/0xA1fa35561AbA3a78d993f898FbAf08736e99B245)​    |
| cbETH                                                                                            | Coinbase   | B         | 750         | 93%                   | ​[Chainlink](https://etherscan.io/address/0x2eab0ac05bb13c811d42d2fc58be3774b15093f8)​   | ​[Fundamental](https://etherscan.io/address/0x79A20a2F58fc68A21DeD72bf20E6ce882B2aADeF)​ |
| ETH+                                                                                             | Reserve    | B         | 500         | 93%                   | ​[Fundamental](https://etherscan.io/address/0xa4fd428191e263453bc802cfad71a31dddf74895)​ | ​[Fundamental](https://etherscan.io/address/0xa4fd428191e263453bc802cfad71a31dddf74895)​ |
| tETH                                                                                             | Treehouse  | C         | 0           | 90%                   | ​[Fundamental](https://etherscan.io/address/0x681b59a4145df0907f7f842d10fc26f147b426ca)​ | ​[RedStone](https://etherscan.io/address/0xe65874ced6c0d9b72c788baef51c3eecdb3dd681)​    |
| ​[Beefy Curve ETH+/WETH](https://app.beefy.finance/vault/curve-eth+-weth)​                       | Beefy      | C         | 750         | 90%                   | ​[ERC4626](https://etherscan.io/address/0xeb1edcc75d8fa8fe56197e26fdf6ba3314959c93)​     | ​[ERC4626](https://etherscan.io/address/0xd3d2e4afa87081ac24b3143d679da9a858077cab)​     |
| ​[Beefy Balancer rETH/WETH](https://app.beefy.finance/vault/balancerv3-ethereum-waethweth-reth)​ | Beefy      | C         | 750         | 90%                   | ​[ERC4626](https://etherscan.io/address/0xf9b51adf90bb15b6e5aca89fc272cab92d6b25a7)​     | ​[ERC4626](https://etherscan.io/address/0xd997435585a5d75cdf6c3c84afc568f19398a68a)​     |
| PT-DETH-23APR2026                                                                                | Pendle     | D         | 0           | 87%                   | ​[Pendle](https://etherscan.io/address/0x8c9e5f0c8dbdb6d41cc591e61bf33332dee5646c)​      | ​[Pendle TWAP](https://etherscan.io/address/0x6ca1d7adaf9886b8fd87d1565fc804c8f4d6b4e1)​ |

{% hint style="info" %}
Quota limits are displayed in native units.
{% endhint %}

#### Key risks

* Collateral, market and price risk
* Oracle manipulation, staleness, or failure
* Liquidity crunches and liquidation cascades during stress events
* Borrower strategy underperformance or insolvency (bad debt)
* Smart contract vulnerabilities and external dependency risks

These risks are actively monitored and managed, but cannot be fully eliminated. For more information, see the [Products Disclaimer](/vaults/resources/legal/gearbox-disclaimer).

### Governance and controls

All critical changes flow through a **layered governance structure** designed for transparency, operational security, and rapid response when needed. Pool parameters (collaterals, oracles, debt limits, etc.) are updated by KPK through the Market configurator, with changes executed via a time-locked process:

* **Market configurator**: Configures curated markets. One configurator can manage multiple markets `0x1b265b97eb169fb6668e3258007c3b0242c7bdbe`
* **Timelock**: Enforces a minimum 24-hour delay between approval and execution of pool changes\
  `0xB680c52D339656C5808Df9c426DF1A2103Ed945a`
* **Emergency admin**: Can perform a limited set of emergency actions (e.g. pause pools) without delay to protect solvency. See [Gearbox Permissionless Docs](https://docs.gearbox.fi/gearbox-permissionless-doc/emergency-roles/emergency-admin) for more details.\
  `0x4ad2419dc6de75c3f57d7b6aa200d494c74c1443`


# wstETH

The KPK wstETH Earn Pool channels deposits into Gearbox’s Credit Account system across selected collateral markets. Yield comes from overcollateralised lending rates on top of the underlying Lido staking rate. Strict utilisation caps, quota limits, and dual-oracle pricing help preserve **liquidity buffers** and mitigate market risks.

<mark style="color:$primary;">The vault</mark> provides exposure to **risk-adjusted wstETH yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://kpk.gearbox.finance/pools/1/0xa9d17f6d3285208280a1fd9b94479c62e0aaba64">https://kpk.gearbox.finance/pools/1/0xa9d17f6d3285208280a1fd9b94479c62e0aaba64</a></td><td><a href="https://app.kpk.io/vaults/gearbox-wsteth-mainnet">https://app.kpk.io/vaults/gearbox-wsteth-mainnet</a></td></tr><tr><td>Live metrics</td><td></td><td><a href="https://dune.com/kpk/kpk-gearbox-vaults#kpk-wsteth-pool">https://dune.com/kpk/kpk-gearbox-vaults#kpk-wsteth-pool</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://app.euler.finance/earn/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245?network=ethereum">Euler</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>wstETH</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0xA9d17f6D3285208280a1Fd9B94479c62e0AABa64#code">0xA9d17f6D3285208280a1Fd9B94479c62e0AABa64</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>90%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>Utilisation target</strong></td><td>92%</td></tr></tbody></table>

### Strategy

Liquidity from this pool is lent to Gearbox borrowers through the Credit Account mechanism. Borrowers use whitelisted LSTs and ERC-4626 vaults as collateral to build leveraged looping strategies, typically targeting AMM, restaking or staking yields.

The pool doesn’t allocate funds itself. Instead, it relies on borrower activity within **pre-defined pool parameters** set by KPK. Yield comes primarily from borrower interest rates.

This pool targets multiple collaterals within the Ethereum ecosystem, enabling leveraged restaking strategies under tight utilisation controls. Native minting and withdrawals are enabled for various assets within Gearbox. Borrowers can enter the native unstaking queue directly through the protocol, allowing positions to unwind gracefully. This avoids common pitfalls in lending markets without native exit support and enables smoother user onboarding, offboarding, and liquidations.

### Risk framework

The pool’s parameters follow KPK’s [Curation Risk Framework](/vaults/infrastructure/risk-framework), which governs asset due diligence, tiering, monitoring, and response procedures. All configuration and parameter updates are recorded in the [Changelog page](https://docs.kpk.io/changelog/). Entries include scope, rationale, and transaction references for full transparency.

#### Risk-tier snapshot

Each asset in a KPK Gearbox market is linked to two price feeds: a main oracle and a reserve oracle. These typically combine a market-rate oracle (e.g., Chainlink or RedStone) and a fundamental oracle (reflecting the underlying exchange or redemption rate). The main oracle is used for account valuation and liquidation checks, while the reserve oracle serves as a safety reference during collateral withdrawals or external calls. This [dual-oracle setup](https://docs.gearbox.fi/gearbox-permissionless-doc/competitive-advantages/dual-oracle-pricing) balances market accuracy with resistance to manipulation, protecting LPs during volatility events such as LST depegs.

| Collateral                                                                                       | Issuer    | Risk tier | Quota limit | Liquidation threshold | Main oracle                                                                               | Reserve oracle                                                                            |
| ------------------------------------------------------------------------------------------------ | --------- | --------- | ----------- | --------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| ETH+                                                                                             | Reserve   | B         | 3,000       | 93%                   | ​[Fundamental](https://etherscan.io/address/0xa4fd428191e263453bc802cfad71a31dddf74895)​  | ​[Fundamental](https://etherscan.io/address/0xa4fd428191e263453bc802cfad71a31dddf74895)​  |
| ​[Beefy Curve ETH+/WETH](https://app.beefy.finance/vault/curve-eth+-weth)​                       | Beefy     | C         | 3,000       | 90%                   | ​[ERC4626](https://etherscan.io/address/0xeb1edcc75d8fa8fe56197e26fdf6ba3314959c93)​      | ​[ERC4626](https://etherscan.io/address/0xd3d2e4afa87081ac24b3143d679da9a858077cab)​      |
| ​[Beefy Balancer rETH/WETH](https://app.beefy.finance/vault/balancerv3-ethereum-waethweth-reth)​ | Beefy     | C         | 3,000       | 90%                   | ​[ERC4626](https://etherscan.io/address/0xf9b51adf90bb15b6e5aca89fc272cab92d6b25a7)​      | ​[ERC4626](https://etherscan.io/address/0xd997435585a5d75cdf6c3c84afc568f19398a68a)​      |
| savETH                                                                                           | Avant     | C         | 3,000       | 91.5%                 | ​[ERC4626 + CL](https://etherscan.io/address/0x71240d0639df41cd2cdbaecd0c9ad93ac893ea4c)​ | ​[ERC4626 + RS](https://etherscan.io/address/0xc4f654b5d47420ea4c428b0eb7d8f8cea143b80d)​ |
| tETH                                                                                             | Treehouse | C         | 500         | 90%                   | ​[Fundamental](https://etherscan.io/address/0x681b59a4145df0907f7f842d10fc26f147b426ca)​  | ​[RedStone](https://etherscan.io/address/0xe65874ced6c0d9b72c788baef51c3eecdb3dd681)​     |
| ​[Beefy Balancer osETH/​WETH](https://app.beefy.com/vault/balancerv3-ethereum-oseth-waethweth)​  | Beefy     | C         | 500         | 90%                   | ​[ERC4626](https://etherscan.io/address/0x1de780e46a8bb022723c8ab694b819f988ec96d3)​      | ​[ERC4626](https://etherscan.io/address/0xfe26523e2233215575f5a6143dc8ba25b749c75f)​      |
| ​[Beefy Balancer tETH/wstETH](https://app.beefy.com/vault/balancerv3-ethereum-teth-wsteth)​      | Beefy     | C         | 500         | 90%                   | ​[ERC4626](https://etherscan.io/address/0x5ed6b7FE88d8bfaFeC0dbADa6DdCcE197C824e4E)​      | ​[ERC4626](https://etherscan.io/address/0x163b55eEc0E39D093fBF844E64E2990fE5dFC9fb)​      |

{% hint style="info" %}
Quota limits are displayed in native units.
{% endhint %}

#### Key risks

* Collateral, market and price risk
* Oracle manipulation, staleness, or failure
* Liquidity crunches and liquidation cascades during stress events
* Borrower strategy underperformance or insolvency (bad debt)
* Smart contract vulnerabilities and external dependency risks

These risks are actively monitored and managed, but cannot be fully eliminated. For more information, see the KPK-curated [Products Disclaimer](/vaults/resources/legal/gearbox-disclaimer).

### Governance and controls

All critical changes flow through a **layered governance structure** designed for transparency, operational security, and rapid response when needed. Market parameters (collaterals, oracles, debt limits, etc.) are updated by KPK through the Market configurator, with changes executed via a time-locked process:

* **Market configurator**: Configures curated markets. One configurator can manage multiple markets `0x1b265b97eb169fb6668e3258007c3b0242c7bdbe`
* **Timelock**: Enforces a minimum 24-hour delay between approval and execution of pool changes\
  `0xB680c52D339656C5808Df9c426DF1A2103Ed945a`
* **Emergency admin**: Can perform a limited set of emergency actions (e.g. pause pools) without delay to protect solvency. See [Gearbox Permissionless Docs](https://docs.gearbox.fi/gearbox-permissionless-doc/emergency-roles/emergency-admin) for more details.\
  `0x4ad2419dc6de75c3f57d7b6aa200d494c74c1443`


# Change log

This page records **all configuration and parameter changes** to KPK-curated Gearbox pools.

Entries are listed in reverse chronological order. For current parameters, see the individual pool pages.

<table><thead><tr><th width="137.6328125">Date</th><th>Scope</th><th width="214.76953125">Change</th><th width="232.828125">Description/Rationale</th><th width="216.65234375">Tx/Reference</th></tr></thead><tbody><tr><td>2026-06-15</td><td>WETH and wstETH markets</td><td>Remove an address from the Emergency Liquidator set</td><td>Routine access-control hygiene; rotated out an Emergency Liquidator key that is no longer in use. Applied at the MarketConfigurator level, so it covers both markets.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiaqaivo73mxiknycp6ci2v4tuvfgqzpd4mhwemfluvgmlxqsa25tq">Tx details</a></td></tr><tr><td>2026-06-15</td><td>wstETH market</td><td>Onboard savETH (Avant) as collateral; IRM update</td><td>Added savETH with 3,000 wstETH quota limit and 91.5% LT via a dedicated Credit Manager (75/3,750 wstETH min/max debt). Adjusted the pool IRM to a higher baseline yield.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreidazqwpin6i452fynoon3bpietkxmqdinpt2sa6q4htsupconn2lu">Tx details</a></td></tr><tr><td>2026-05-27</td><td>WETH market</td><td>rETH oracle swap (Main ↔ Reserve) and IRM update</td><td>Setting Fundamental pricing as main feed to avoid unwanted liquidations, and bumping the max ETH borrow rate to incentivize repayment on stress scenarios.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicdpz3pjzrwfji3dbdodpvwqnsnd2kwuld3cwlchzvq3s6may67za">setFeeds &#x26; setInterestRateModel</a></td></tr><tr><td>2026-05-18</td><td>Emergency Admin</td><td>Added new signer to the Emergency Admin Safe</td><td>New signer onboarded, threshold 2/5</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443&#x26;id=multisig_0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443_0x659386458b07b77f9ae4aab3beecd20a6cf57eb36e1368d2b4cf2fa5885c800d">addOwnerWithThreshold</a></td></tr><tr><td>2026-05-09</td><td>wstETH market</td><td>Re-enable three Beefy LP strategies (rETH/WETH, osETH/WETH, tETH/wstETH) at lower quotas than pre-Kelp-exploit; onboard tETH (Boosted Beefy CM, 90% LT).</td><td>Re-open LST/LRT looping plays on the market intended to host them, following Aave WETH liquidity restoration and continued rsETH recovery. Quotas held below pre-Kelp-exploit levels given thinner DEX liquidity for osETH and tETH.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicjpyp4ge35qgw33nkxxpe7momxskcm4rf3jryw5nanskksvyxdoq">Tx details</a></td></tr><tr><td>2026-05-09</td><td>WETH market</td><td>Broad single-asset and LP quota cuts (~80–85%); permanent offboard of DETH and Beefy tETH/wstETH; remaining LST/LRT plays moved to 0-quota wind-down.</td><td>Concentrate vanilla assets in the WETH market and push LST/LRT leverage to the wstETH market. Wind-down (0 quota) rather than hard offboard preserves the ability for healthy positions to close without forced liquidation.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicjpyp4ge35qgw33nkxxpe7momxskcm4rf3jryw5nanskksvyxdoq">Tx details</a></td></tr><tr><td>2026-05-09</td><td>WETH market</td><td>Set ezETH quota to 0 (full offboard)</td><td>Removed due to low utilization; kept in the risk-tier snapshot since minor borrow activity remains.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicjpyp4ge35qgw33nkxxpe7momxskcm4rf3jryw5nanskksvyxdoq">Tx details</a></td></tr><tr><td>2026-05-08</td><td>WETH and wstETH markets</td><td>Switch ETH+ main price feed to Fundamental oracle</td><td>Deprecated RedStone ETH+ market feed; replaced with the Fundamental oracle on both markets to improve manipulation resistance and maintain consistency with the reserve feed.</td><td>WETH: <a href="https://etherscan.io/tx/0x677a394ef4b4a8bb271c3adeebff5dc3998210ec97d34ff7c7bda69c7b57c894#eventlog">setPriceFeed</a><br>wstETH: <a href="https://etherscan.io/tx/0x26539c998c05bbd84795cc5f2568ce8051beaca385da6ac5c906b5a1a3a065a9">setPriceFeed</a></td></tr><tr><td>2026-04-21</td><td>ETH market</td><td>Flatten IRM on ETH pool.</td><td>Reduce strain on borrowers given post-rsETH exploit market contagion.</td><td><a href="https://etherscan.io/tx/0xddd0454e89e6ac581e953e3b7e0d0ac4ac6d82130c7dd04e46d689508d6539d8#eventlog">SetInterestRateModel</a></td></tr><tr><td>2026-04-18</td><td>WETH and wstETH markets</td><td>Emergency responses to the Kelp DAO rsETH exploit.</td><td>Removed collaterals with potential direct or indirect exposure to affected protocols.</td><td>WETH: <a href="https://etherscan.io/tx/0x24d1a3ecba4780af9a3b807354aafcd8cef9e710f9ac36e6bbb4fd86725de0ac">rsETH</a>, <a href="https://etherscan.io/tx/0x0e26074488921caca1c5aac424b01d7afa4aefc3a40379a9e3ced42a680430d5">rsWETH_unstaking</a>, <a href="https://etherscan.io/tx/0xb7f7b6d41acb623b47781aa28dd662f34cc87cd8def19ab10322e8ce5a6a2077">DETH</a>, <a href="https://etherscan.io/tx/0x44d094f01957f76acb54eb2ae96482965241ad78d1651011277067166f17f008">PT-DETH</a>, <a href="https://etherscan.io/tx/0x0539742d9f8ea944c013e0f43adb0db9ec31b4cd6f499720e35652114aceaccf">Beefy-rETH</a>, <a href="https://etherscan.io/tx/0xca8039344c6a3c263f2cf2f8d1b956b45101b3309c33803d010b25e167c5a4b1">tETH</a>, <a href="https://etherscan.io/tx/0x9e854ad1a6f05871dca349e5f382b26fe3b04a33d85cf6a9f3a807b9ea4af665">Beefy-tETH</a>, <a href="https://etherscan.io/tx/0xa2454810b709f96ee35fa1350f177a6694f2574d41f4cf5e705546e3e082d967">Beefy-osETH</a>. wstETH: <a href="https://etherscan.io/tx/0x0acfeea7f22b7b8f1e90d968cfdbab90cfcc81a136ec7ee31e7bf877ac6c61f8">PT-DETH</a>, <a href="https://etherscan.io/tx/0x0accc1ed2824d8c921da218e3a0bb0f160600995dc5df9bb200a58c5263604da">DETH</a>, <a href="https://etherscan.io/tx/0x77f31b15635ffcb4a2e8457c92f77d75434804b1fdea184751c4ae169fbaad9c">Beefy rETH</a>, <a href="https://etherscan.io/tx/0x82b643c81da3a4003059cc5f40caed36dc8b2cb7679172678dee3e8715c6a7e1">Beefy-tETH</a>, <a href="https://etherscan.io/tx/0x340eee6959b3749bbfc84ed9252906016cc0718c6f6c6e77d8104b9ef9a107ac">Beefy-osETH</a></td></tr><tr><td>2026-03-03</td><td>WETH market</td><td>Switch rETH main price feed to Redstone.</td><td>Upcoming deprecation of Rocketpool's CL feed.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiejwjm76i46fkjn6awsvm5uy6lkli3mafjbltjfdwe67eiw4bljwm">Tx details</a></td></tr><tr><td>2026-02-25</td><td>WETH and wstETH markets</td><td>Onboard <a href="https://app.beefy.com/vault/balancerv3-ethereum-teth-wsteth">tETH-wstETH LP (Aura) Beefy strategy</a> as collateral in both markets (90% LT, 1,500 quota).</td><td>Enable new looping opportunities; and configured the required swap routing/adapters for reliable execution.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiauyix2vu3fsagydnsuwokhsvs6gfivxrzfiu6blpkyvhgeaz6nx4">Tx details</a></td></tr><tr><td>2026-02-06</td><td>WETH and wstETH markets</td><td>Onboard PT-DETH23APR2026 and native rsETH integration</td><td>Included longer expiry PT for DETH, re-enabled borrowing on DETH strategy and enable native rsETH minting and redeeming.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreibgiudwx7ypb32sqhnaycybtiab5e6czvzq7gynhkgx33goxrufii">Tx details</a>,<br><a href="https://safe.gearbox.finance/txs/?cid=bafkreigviv6tk74zrnwduccciorxhzglvr33onzoghyh3fayp4kk3pishe">Tx details</a></td></tr><tr><td>2026-01-29</td><td>WETH and wstETH markets</td><td>Re-enabling DETH</td><td>Re-enabled DETH following Makina's <a href="https://x.com/makinafi/status/2015858136985239960?s=20">re-activation of machines</a> (v1.1).</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiedksjb6e6w6iezsheviv6rd2derdqhq3jm3tabkctop5qdzr5vj4">Tx details</a></td></tr><tr><td>2026-01-25</td><td>WETH market</td><td>Re-enabling rsETH</td><td>Re-enabled rsETH as collateral after precautionary forbid.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiggvvhylgkd46tzyiabcwx77f6l5renq3hxjhbopxsaj7hebenr3m">Tx details</a></td></tr><tr><td>2026-01-21</td><td>WETH and wstETH markets</td><td>Calling updateRates on Tumbler.</td><td>Final step in osETH-aWETH LP onboarding (applying interest rate).</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreig4z23oly2hi4mqp3ypxztdllxto3uud3ospfx263rtkvh3tsbnv4">Tx details</a></td></tr><tr><td>2026-01-20</td><td>WETH and wstETH markets</td><td>Forbid DETH and PT-DETH tokens and forbid borrowing in the CM.</td><td>Precautionary measure, in response to DUSD Curve <a href="https://www.coindesk.com/markets/2026/01/20/makina-loses-usd4-1-million-in-exploit-tied-to-price-feed-manipulation">exploit</a>, to reduce risk.</td><td><a href="https://app.safe.global/transactions/tx?id=multisig_0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443_0xad762a8f619c99675a062d59b495d615a5b6d87d8ec961a6ff04fb4a0231b473&#x26;safe=eth:0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443">Tx details</a></td></tr><tr><td>2026-01-16</td><td>WETH &#x26; wstETH markets</td><td>Onboard <a href="https://app.beefy.com/vault/balancerv3-ethereum-oseth-waethweth">osETH-aWETH LP (Aura)</a> Beefy strategy; reduce ezETH quota to 1000; update Balancerv3 swap routes; set Effective Rate for high-yielding Beefy vaults</td><td>Enable new looping opportunities; reduce risk; improve swap execution; balance lending and borrowing rates.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreig3iaio3qg4wnu7hmu6tfwmazdbihotngv5i5qs5v3yqldugnccyu">Tx details</a></td></tr><tr><td>2026-01-15</td><td>WETH market</td><td>Update Balancerv3 Gateway to v3.11</td><td>Improve swap execution.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiaayztdgps4ug6636frs5oisj3q7ukifmzddbv2dxwspb4bneix6q">Tx details</a></td></tr><tr><td>2026-01-15</td><td>WETH &#x26; wstETH markets</td><td>Set quota and LT to 0 for RE7LRT and pzETH &#x26; add ERC4626 rETH Beefy adapter &#x26; osETH-rETH swap routes.</td><td>Reduce risk by offboarding unused assets; improve swap execution &#x26; prepare for new asset onboarding.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiajmvfu7wpaooomlwekyces32op7nhoysrdwvybndxmopnv4tl4ve">Tx details</a></td></tr><tr><td>2026-01-13</td><td>WETH &#x26; wstETH markets</td><td>Onboarded <a href="https://app.beefy.finance/vault/balancerv3-ethereum-waethweth-reth">rETH-aWETH LP (Aura)</a> Beefy strategy and set rstETH quota to 0.</td><td>Enable new looping opportunities &#x26; offboarded unused assets.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreigb7feotmafj4ahto7iqyz4j3ryzcjx5qtmewi3lllura46tej6vu">Tx details</a></td></tr><tr><td>2026-01-09</td><td>WETH and wstETH markets</td><td>New Balancer route for rETH.</td><td>Includes the Aave boosted <a href="https://balancer.fi/pools/ethereum/v3/0x1ea5870f7c037930ce1d5d8d9317c670e89e13e3">rETH/WETH </a>pool to improve rETH swaps.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiaqqlezzsecms66awgg7gb2dqm5o4qjf6bxtore35h3jhuh45my3e">Tx details</a></td></tr><tr><td>2026-01-08</td><td>WETH and wstETH markets</td><td>New Balancer route for wstETH.</td><td>Includes the Fluid boosted <a href="https://balancer.fi/pools/ethereum/v3/0x6b31a94029fd7840d780191b6d63fa0d269bd883">wstETH/WETH</a> pool to route wstETH swaps.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreib2vptbp7w6npbxjyaeafeueo3juzv3qfyvrxnvrhd6xnu345ow3m">Tx details</a>,<br><a href="https://safe.gearbox.finance/txs/?cid=bafkreiax35oibr7nhtded3b7a76n3f7pso4rvcb5ywk6edcuambawrlnqi">Tx details</a></td></tr><tr><td>2026-01-07</td><td>wstETH market</td><td>Unpause Credit Managers Tier #I and Tier #II</td><td>Unpause of CMs after successful rstETH offboarding.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicc67lobhpg7w7kbbyk4mvkqxjo53n3odbiamjao2znqgpigmp2iy">Tx details</a></td></tr><tr><td>2025-12-30</td><td>WETH market</td><td>Update price feed for B-rETH-STABLE to zero.</td><td>Improved monitoring &#x26; rETH swaps.</td><td><a href="https://safe.gearbox.finance/emergency/tx/?chainId=1&#x26;mc=0x1B265B97EB169fB6668E3258007C3b0242C7bDBE&#x26;action=ORACLE%3A%3AsetPriceFeed&#x26;params=%7B%22pool%22%3A%220x9396DCbf78fc526bb003665337C5E73b699571EF%22%2C%22priceFeed%22%3A%220xfe1ec406d777b0c4e2bf5e12d0f645721b2b86be%22%2C%22token%22%3A%220x1e19cf2d73a72ef1332c882f20534b6519be0276%22%7D&#x26;nonce=140">Tx details</a></td></tr><tr><td>2025-12-20</td><td>wstETH market</td><td>Pause CMs containing rstETH.</td><td>Planned asset offboarding.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443&#x26;id=multisig_0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443_0xa20b2e7bd59d39aee7cfd4432016c3561945610e6e6500729d10ce59a2ff6dd5">Tx details</a></td></tr><tr><td>2025-12-20</td><td>WETH and wstETH markets</td><td>Added Gearbox DAO addresses as Emergency Liquidators.</td><td>Improved liquidation process, for <a href="https://snapshot.box/#/s:gearbox.eth/proposal/0xf1afeed54d0878cd2d8979ad710b437f8d451495245cc1127dc602bb743f6dda">planned</a> rstETH offboarding.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreiay4zidyhtzxhsaavhd3yonjeamcdh32wng64e6y4wutnlhnzkauy">Tx details</a></td></tr><tr><td>2025-12-20</td><td>WETH and wstETH markets</td><td>Ramp rstETH LT to zero, over a 48hr period &#x26; lowered Quota for other Mellow assets (i.e. RE7LRT and pzETH).</td><td>Second step in offboarding rstETH. Lower general risk exposure.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreigsea55ng7k7n72qrfkcyk7vnd5bi5iums36t75bmksq2gltdlhie">Tx details</a></td></tr><tr><td>2025-12-19</td><td>WETH and wstETH markets</td><td>Set rstETH quota to zero</td><td>First step in offboarding rstETH, due to a planned <a href="https://snapshot.box/#/s:gearbox.eth/proposal/0xf1afeed54d0878cd2d8979ad710b437f8d451495245cc1127dc602bb743f6dda">contract update</a>.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443&#x26;id=multisig_0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443_0x064bbc77a0c15842f731b6083b50701ea1ac87a655fc54f700a225b371ad80b1">Tx details</a></td></tr><tr><td>2025-12-12</td><td>WETH and wstETH markets</td><td>Onboard <a href="https://app.makina.finance/strategy/DETH">DETH</a>, <a href="https://app.pendle.finance/trade/markets/0x937c7868824ae53db3b7c634de209fb7a74e362c/swap?view=pt&#x26;chain=ethereum">PT-DETH-29JAN2026</a> and <a href="https://app.beefy.finance/vault/curve-eth+-weth">wmooCurveETH+-WETH</a> as collateral in both markets and improve DEX routing via Balancer v3.</td><td>Enable new looping opportunities in both markets, with improved wstETH trade execution.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafybeihywlrvt54yjois5ks7o44u3aoisptlbkkbhdk5qj7zyfpsoiznc4">Tx details</a></td></tr><tr><td>2025-12-12</td><td>wstETH market</td><td>Enable ETH+ as collateral, with similar parameters as in the ETH market</td><td>Enable new looping opportunities.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafybeihywlrvt54yjois5ks7o44u3aoisptlbkkbhdk5qj7zyfpsoiznc4">Tx details</a></td></tr><tr><td>2025-12-04</td><td>WETH market</td><td>Update LT for stETH and wstETH in pzETH and RE7LRT CM</td><td>Improve configuration for smoother native Mellow redemptions.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreia5i6mdwhd2ifhayxrdidkpt5s3sc6ovtwr4e6ayyspudeg7apng4">Tx details</a></td></tr><tr><td>2025-11-12</td><td>WETH market</td><td>ezETH oracle update</td><td>Chainlink deprecated ezETH oracle; moved to Redstone market price feed.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreicef73mjswnpzziovfo2ek6z6z2cqgah6joq3ns77c27mkbp5zrqu">Tx details</a></td></tr><tr><td>2025-11-07</td><td>WETH market</td><td>New interest rate model (IRM) &#x26; updated reserve oracles for ezETH, weETH, rsETH.</td><td>IRM adjustment to reduce max ETH borrow rate; switch to fundamental oracles that query the underlying exchange rates directly.</td><td><a href="https://safe.gearbox.finance/txs/?cid=bafkreias7estqs345ubpynwjuj73ntpnbs7iyt5zptll7us3o2lkah6b7i">Tx details</a></td></tr><tr><td>2025-11-03</td><td>WETH market</td><td>Disabled BALANCER_VAULT adaptor in Tier #1 and Tier #2</td><td>Preventive measure in response to confirmed Balancer v2 exploit.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443&#x26;id=multisig_0x4Ad2419dc6DE75C3f57d7b6Aa200D494c74c1443_0xad2ffacd563a7c96f03ca8db85dd4c30d32cc7bfae12283d04e5d105076ae7c1">Tx details</a></td></tr><tr><td>2025-10-31</td><td>WETH and wstETH markets</td><td>Add <a href="https://docs.gearbox.fi/gearbox-permissionless-doc/competitive-advantages/dual-oracle-pricing-and-loss-policy#loss-policy-if-you-chose-to-use-market-prices">Loss Policy</a>.</td><td>Added Loss Policy using Fundamental Oracles for both WETH and wstETH markets</td><td><a href="https://etherscan.io/tx/0x927edc91e5a6b35bd145d49d615ba06500c3361adf35541583267a44993f679e">Tx details</a></td></tr><tr><td>2025-10-23</td><td>WETH market</td><td>Update wstETH reserve oracle.</td><td>Re-enabling reserve oracle for wstETH after yesterday's change.</td><td><a href="https://permissionless-safe.gearbox.foundation/txs/?cid=bafkreievqmsvehyudx77gizd6devaaohu7h5o3zrhccbvi7arqh6fzwkua">Tx details</a></td></tr><tr><td>2025-10-22</td><td>WETH and wstETH markets</td><td>Update main oracle for wstETH.</td><td>Avoid potential liquidations on rstETH/wstETH and wstETH/ETH positions.</td><td><a href="https://permissionless-safe.gearbox.foundation/emergency/tx/?chainId=1&#x26;mc=0x1B265B97EB169fB6668E3258007C3b0242C7bDBE&#x26;action=ORACLE%3A%3AsetPriceFeed&#x26;params=%7B%22pool%22%3A%220xA9d17f6D3285208280a1Fd9B94479c62e0AABa64%22%2C%22priceFeed%22%3A%220x587660d2d2ccba5efd0594ccfda03c5adc1a3dc9%22%2C%22token%22%3A%220x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0%22%7D&#x26;nonce=90">Tx1</a> ,<a href="https://permissionless-safe.gearbox.foundation/emergency/tx/?chainId=1&#x26;mc=0x1B265B97EB169fB6668E3258007C3b0242C7bDBE&#x26;action=ORACLE%3A%3AsetPriceFeed&#x26;params=%7B%22pool%22%3A%220x9396DCbf78fc526bb003665337C5E73b699571EF%22%2C%22priceFeed%22%3A%220x587660d2d2ccba5efd0594ccfda03c5adc1a3dc9%22%2C%22token%22%3A%220x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0%22%7D&#x26;nonce=91">Tx2</a></td></tr><tr><td>2025-10-21</td><td>WETH and wstETH markets</td><td>Configure MELLOW_CLAIMER adapters in newest CMs.</td><td>Enable native unstaking of Mellow assets.</td><td><a href="https://permissionless-safe.gearbox.foundation/txs/?cid=bafkreidt7qqgj6aweehxnetvpwmyrynvspryabhv3kjcxp5bzkle5c3jrq">Tx details</a></td></tr><tr><td>2025-10-17</td><td>WETH and wstETH markets</td><td>Create new Credit Managers and set Debt Limit to 0 on existing ones.</td><td>Updated Interest Rate fee for all new borrowers.</td><td><a href="https://permissionless-safe.gearbox.foundation/txs/?cid=bafybeibzc3hedi65hg42j567df3konql6wc2x6ieamuxvrl5j7fc5yrqo4">Tx details</a></td></tr><tr><td>2025-10-09</td><td>WETH and wstETH markets</td><td>Add PAUSABLE_ADMIN role in Market Configurator</td><td>Improved emergency pause role, for active Pools and Credit Managers.</td><td><a href="https://permissionless-safe.gearbox.foundation/txs/?cid=bafkreihshp2nps3epi7cze7j5jqhe7x5ol7gfutbj7tcfut63e2xvbbd2a">Tx details</a></td></tr></tbody></table>


# Euler

KPK vaults on Euler

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Prime RWA</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/euler/ethereum/usdc-rwa">USDC Prime RWA</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGJT8IKcX8OTU2sVwY0Yy%2FEuler_USDC_L.png?alt=media&amp;token=ec77dafc-df77-446c-9415-f788e73e8562">Euler_USDC_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGdEmvsuBNiJ2gTdQJ1iP%2FEuler_USDC_D.png?alt=media&amp;token=487ab5e6-33ba-4609-97f7-b9a8e8bd2283">Euler_USDC_D.png</a></td></tr></tbody></table>

### Protocol overview

[Euler](https://www.euler.finance/) is a modular lending protocol. It combines **Euler Earn**, the Euler Vault Kit (**EVK**), and the Ethereum Vault Connector (**EVC**). Euler Earn is a non-custodial ERC-4626 meta-vault that accepts a single deposit asset and allocates it across approved strategies; it is built on a fork of Morpho v1's MetaMorpho codebase, adapted by the Euler team, so readers familiar with Morpho v1 will recognise a similar governance and timelock structure. EVK vaults are ERC-4626 credit vaults with borrowing functionality. The EVC coordinates collateral management and liquidation flows across vaults. Together, this stack allows KPK to curate not only top-level allocations but also the structure and risk controls of the underlying borrow markets on which those allocations depend.

{% hint style="success" %}
**Live metrics:** Track TVL, borrow APY, supply APY, utilisation and more for KPK Euler vaults on Dune: <https://dune.com/kpk/kpk-euler-vaults>
{% endhint %}

### Curator model

KPK’s role on Euler is broader than on Morpho. In addition to managing the **Euler Earn** vault, KPK curates the core governed borrow markets that underpin its Euler strategies. This includes setting caps and LTV/LLTV parameters, configuring oracle routing, and defining operational controls for the markets under KPK governance. All decisions follow the [**Risk Framework**](/vaults/infrastructure/risk-framework), which governs eligibility criteria, risk tiering, and monitoring cadence.

KPK’s Euler curation focuses on three areas:<br>

1. <mark style="color:$primary;">**Core market design and withdrawal reliability**</mark>\
   KPK does not only choose allocations. It also curates the core borrowing markets the Earn vault is designed around. That means selecting approved markets, assigning risk tiers, setting exposure limits, configuring oracle routing, and managing the conditions under which capital can move between markets. Depending on the vault, enabled markets may also include complementary yield sources, KPK-curated or third-party-curated, that improve capital efficiency without altering the vault’s overall risk posture.
2. **Specialised market mechanics**\
   For products with non-standard pricing or access patterns, KPK governs additional market-level components. This includes cyclical Interest Rate Models for calendar-aligned fixed-rate markets, access-control hooks that restrict who can supply to a borrow market, and, for transfer-restricted RWAs, compliant ERC-4626 collateral wrappers governed by the asset issuer. KPK does not govern issuer-controlled wrappers; it curates the markets that accept them as collateral.
3. <mark style="color:$primary;">**Real-time automation (agents) and monitoring**</mark>\ <mark style="color:$primary;">Vaults are monitored continuously by agents operated by KPK. They are deterministic programs that execute</mark> <mark style="color:$primary;">**whitelisted functions**</mark> <mark style="color:$primary;">through KPK’s</mark> [<mark style="color:$primary;">Permissions Layer</mark>](https://kpk.io/blog/permissions-layer)<mark style="color:$primary;">. Agents monitor utilisation, liquidity depth, APY shifts, oracle health, and price divergence versus reference venues. When conditions change, they can reduce or disable exposure to a market, increase idle funds, or rebalance across enabled markets within predefined limits.</mark>
   * <mark style="color:$primary;">**Rebalancing agent:**</mark> <mark style="color:$primary;">improves capital efficiency by reallocating across approved markets using a rules-based approach (e.g. tier- and cap-aware water-filling), subject to liquidity and safety checks.</mark>
   * <mark style="color:$primary;">**Exit agent:**</mark> <mark style="color:$primary;">responds to risk alerts (e.g. oracle staleness or divergence) within seconds by reducing or disabling exposure, increasing withdrawable liquidity, and prioritising safe exits within predefined limits.</mark>\
     \ <mark style="color:$primary;">This combination supports both steady, rules-driven allocation and fast incident response. For the technical design and allowed actions, see</mark> [<mark style="color:$primary;">Automation</mark>](/vaults/infrastructure/automation)<mark style="color:$primary;">.</mark>

### Vault architecture

KPK’s Euler setup can involve up to five layers, depending on the product:

* **Euler Earn vault**: the user-facing ERC-4626 vault that accepts a single deposit asset and allocates across approved strategies.
* **Core KPK-governed borrow markets**: EVK credit vaults where KPK governs key market parameters such as caps, LTV/LLTV, oracle routing, and operational controls.
* **Market mechanics layer**: where the product requires it, KPK installs specialised components on the borrow markets, including cyclical Interest Rate Models for fixed-rate markets and access-control hooks that restrict who can supply.
* **Complementary markets**: where appropriate, the Earn vault may also allocate to selected markets curated by KPK or third parties that fit the strategy’s risk profile.
* **Issuer-governed collateral vaults**: compliant ERC-4626 wrappers around certain transfer-restricted RWA assets, used as collateral within the underlying borrow markets.

#### Governance

Governance on Euler spans the **Euler Earn** vault, the underlying borrow markets, and, where applicable, shared infrastructure such as the Oracle Router. Exact role holders, timelocks, and scoped agent permissions are documented on each vault page.

### Vault configuration

Each vault page documents the enabled markets, allocation caps, risk tiers, LTV/LLTV parameters, oracle setup, governance addresses, and agent permissions for that strategy. Where relevant, it also links the collateral vaults used in the underlying market structure.

* [Ethereum](/vaults/vaults/euler/ethereum)
  * [USDC Prime RWA](/vaults/vaults/euler/ethereum/usdc-rwa)


# Ethereum

KPK-curated Euler vaults on Ethereum.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Prime RWA</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/euler/ethereum/usdc-rwa">USDC Prime RWA</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGJT8IKcX8OTU2sVwY0Yy%2FEuler_USDC_L.png?alt=media&amp;token=ec77dafc-df77-446c-9415-f788e73e8562">Euler_USDC_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGdEmvsuBNiJ2gTdQJ1iP%2FEuler_USDC_D.png?alt=media&amp;token=487ab5e6-33ba-4609-97f7-b9a8e8bd2283">Euler_USDC_D.png</a></td></tr></tbody></table>


# USDC Prime RWA

The USDC Prime RWA vault allocates USDC across selected Euler markets backed by tokenised RWA collateral, with a focus on the Securitize ecosystem. Yield comes from overcollateralised lending rates in the underlying markets. Exposure is capped per market, with **24/7 automation** and liquidity buffers to preserve smooth withdrawals and stable, risk-adjusted returns.\
\
The vault provides exposure to **RWA-based yield streams that are less correlated with broader crypto market conditions.**

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/euler-earn-rwa-usdc-mainnet">https://app.kpk.io/vaults/euler-earn-rwa-usdc-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-euler-vaults#kpk-usdc-prime-rwa">https://dune.com/kpk/kpk-euler-vaults#kpk-usdc-prime-rwa</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://app.euler.finance/earn/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245?network=ethereum">Euler</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245">0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>~12.5% withdrawable buffer</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Euler markets and allocates across them under KPK-defined risk limits. Markets are enabled only after passing due diligence under KPK’s [**Risk Framework**](/vaults/infrastructure/risk-framework). Each enabled market is assigned a risk tier and a per-market cap to limit concentration and preserve diversification.

Vault management is [**fully automated**](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a pause agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields ([Monitoring and Response](/vaults/infrastructure/risk-framework#monitoring-and-response)). For the specific scoped permissions and allowed actions, see [Governance and controls](#governance-and-controls) below.

The strategy targets tokenised RWA collateral markets on Euler, with a focus on the Securitize ecosystem. Three markets are enabled today:

* [VBILL](https://securitize.io/primary-market/vaneck-vbill): VanEck-managed fund investing in cash, US Treasury bills, and repurchase agreements, with State Street as custodian
* [STAC](https://securitize.io/primary-market/Securitize-BNY-CLO-Fund): Securitize-launched fund investing in a diversified portfolio of US AAA Collateralised Loan Obligations (CLOs), with BNY as service partner (custodian and sub-adviser)
* [HINC](https://id.securitize.io/primary-market/opportunities/447): Neuberger Berman sub-advised fund investing primarily in US high-yield corporate bonds and BB-rated CLO debt tranches, with BNY as custodian

Additional Securitize-issued markets may be added over time after review under KPK’s Risk Framework.

<figure><picture><source srcset="/files/Ot60Ilc0B3U0ArO7nXI5" media="(prefers-color-scheme: dark)"><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FpP467HilpGQuacXyZCC4%2FEuler%20setup_%20USDC%20Earn%20vault%20routing%20%2B%20DS-backed%20borrowing_L.png?alt=media&amp;token=000294be-f13e-48da-a8e1-78dda2b6a28e" alt="USDC Prime RWA routing: USDC deposits flow into the Earn vault, which routes into two borrowable USDC vaults (USDC Vault 1 and USDC Vault 2). Each borrowable vault accepts a Securitize-issued DS-token vault as collateral (STAC token vault and VBILL token vault), where whitelisted DS holders deposit to borrow USDC."></picture><figcaption><p>USDC Earn vault routing and DS-backed borrowing collaterals.</p></figcaption></figure>

Each of these markets accepts only its own Securitize-issued collateral, which is transfer-restricted at the token contract to holders whitelisted by Securitize as transfer agent. Every borrower in them is therefore KYCed, and the collateral cannot leave the permissioned set even through a liquidation, since liquidators must be whitelisted as well. This applies to the KPK-curated markets only, not to the overflow markets described below.

The vault may also allocate to two Sentora-curated overflow markets: [RLUSD](https://app.euler.finance/borrow/0x9bD52F2805c6aF014132874124686e7b248c2Cbb/0xaF5372792a29dC6b296d6FFD4AA3386aff8f9BB2?network=1) and [PYUSD](https://app.euler.finance/borrow/0xAB2726DAf820Aa9270D14Db9B18c8d187cbF2f30/0xba98fC35C9dfd69178AD5dcE9FA29c64554783b5?network=1). RLUSD is Ripple’s USD-backed stablecoin on Ethereum, backed by segregated reserves of cash and cash equivalents and issued under regulatory oversight. PYUSD is PayPal’s USD-backed stablecoin, issued by Paxos Trust Company under New York State Department of Financial Services supervision. When utilisation in the Securitize-linked markets is high, these allocations act as an instant-redemption buffer that supports withdrawal liquidity; when it is low, they maintain a baseline level of yield. Holding two overflow markets rather than one lets the rebalancing agent move between them as borrow rates diverge. Sentora sets the collateral lists and LTV parameters on these markets; KPK sizes its exposure accordingly, monitors them continuously, and reallocates or exits automatically if their risk profile changes.

The NAV that prices the Securitize-linked markets is published onchain by RedStone as a fundamental feed. Its read history is public per asset: [VBILL](https://app.redstone.finance/push-feeds/VBILL_ETHEREUM_FUNDAMENTAL/ethereumMultiFeed), [STAC](https://app.redstone.finance/push-feeds/STAC_FUNDAMENTAL/ethereumMultiFeed), and [HINC](https://app.redstone.finance/push-feeds/HINC_ETHEREUM_FUNDAMENTAL/ethereumMultiFeed).

The Earn vault charges no performance or management fee. Yield from the underlying markets flows directly to depositors, net of the interest fee charged at each underlying market: KPK takes 5% of borrow interest at the VBILL and STAC markets and 2.5% at the HINC market (other curators, such as Sentora on the RLUSD and PYUSD overflow markets, set their own, typically up to 10%).

### Risk framework

This vault follows KPK’s [**Risk Framework**](/vaults/infrastructure/risk-framework) for market selection, tiering, and ongoing monitoring. Reviews consider onchain and offchain diligence, together with external signals where relevant. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="156.8203125">Market exposure</th><th width="103.73046875">Issuer</th><th width="101.0078125">Risk tier</th><th width="140.7265625" align="right">Allocation cap</th><th width="111.192626953125" align="right">LTV/LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.euler.finance/borrow/0xCf4846E0d8A8667c516B844EB72A4d6e7430101D/0x2Ff596321782FE034102f55af5ad707A4Ce0d6a7?network=1">VBILL/USDC (eUSDC-105)</a></td><td>Securitize</td><td>A</td><td align="right">75%</td><td align="right">90/93%</td><td><a href="https://etherscan.io/address/0x2367f7073E1cD0ce01A63bE37D261a7DDE59b63c">RedStone</a></td></tr><tr><td><a href="https://app.euler.finance/borrow/0x1cFC56665c2718454e8dDf975dC37aF0bc68B5aA/0x8b2d7534Ffcf6c2a9226f439CDaC26c6666E97a9?network=1">STAC/USDC (eUSDC-106)</a></td><td>Securitize</td><td>B</td><td align="right">60%</td><td align="right">82/88%</td><td><a href="https://etherscan.io/address/0xe3F4CA15A085AC6ef8d2461eFC677dD4C1AB3B34">RedStone</a></td></tr><tr><td><a href="https://app.euler.finance/borrow/0x4ab4bEab100d028deCB6835c8f6916031D73793e/0xd06836F33C9c3c1D372097952C0e60240B1a3fDE?network=1">HINC/USDC (eUSDC-137)</a></td><td>Securitize</td><td>C</td><td align="right">50%</td><td align="right">66/74%</td><td><a href="https://etherscan.io/address/0x45e53ab82a2f270f7ac4d5b7e05a115b667acb98">RedStone</a></td></tr><tr><td><a href="https://app.euler.finance/borrow/0x9bD52F2805c6aF014132874124686e7b248c2Cbb/0xaF5372792a29dC6b296d6FFD4AA3386aff8f9BB2?network=1">RLUSD/USDC (eUSDC-70)</a></td><td>Ripple</td><td>A</td><td align="right">90%</td><td align="right">89/91%</td><td><a href="https://etherscan.io/address/0x3bDcB804Fd42Ccb2B7Cf329fa07724bEcB872970">Chainlink</a></td></tr><tr><td><a href="https://app.euler.finance/borrow/0xAB2726DAf820Aa9270D14Db9B18c8d187cbF2f30/0xba98fC35C9dfd69178AD5dcE9FA29c64554783b5?network=1">PYUSD/USDC (eUSDC-80)</a></td><td>Paxos</td><td>A</td><td align="right">65%</td><td align="right">89/91%</td><td><a href="https://etherscan.io/address/0x27895A6295a5117CB989d610DF1Df39DC2CDBf8F">Chainlink</a></td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Euler, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [**Euler Disclaimer**](/vaults/resources/legal/euler-disclaimer).

### Governance and controls

Critical actions follow a layered process designed for transparency, security, and timely response.

{% tabs %}
{% tab title="Governance changes (timelocked)" %}
Changes that can expand risk or permissions, such as role updates, enabling a new market, or increasing market caps, execute via KPK multisigs and a **3-day timelock**. The Guardian can intervene during the timelock window if required.
{% endtab %}

{% tab title="Operational changes (not timelocked)" %}
Operational actions within pre-approved bounds are delegated through scoped permissions. These include `reallocate()` on the Earn vault, `submitCap()` as part of Earn-level de-risking, and `setCaps()` on the KPK-curated USDC markets to support faster reduction of exposure where needed. This allows agents to respond within seconds.
{% endtab %}
{% endtabs %}

KPK's governance applies to the Earn vault and the borrowable USDC markets only. The Securitize collateral vaults (VBILL, STAC, and other Securitize-issued [ERC4626 wrappers](https://github.com/euler-xyz/evk-periphery/blob/master/src/Vault/deployed/ERC4626EVCCollateralSecuritize.sol)) are governed by Securitize, not KPK.

<details>

<summary><strong>Roles, multisigs and agent permissions</strong></summary>

#### Roles

**Earn Vault**

* **Owner & Guardian:** [Security Council Safe](https://app.safe.global/transactions/history?safe=eth:0x354C92aF243d53A24feb3dFF20372Af7b7c47478) (5/8)\
  `0x354C92aF243d53A24feb3dFF20372Af7b7c47478`
* **Curator & Allocator:** [Curator Safe](https://app.safe.global/transactions/history?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F), with a [Permissions Layer](https://kpk.io/blog/permissions-layer) for agents (2/5)\
  `0x1572063377a9a4f8065BD7bA0D7fa135cd13051F`

{% hint style="info" %}
KPK does not use Euler's public Allocator role, reducing the attack surface. Automation is instead scoped through KPK’s Permissions Layer and Euler's roles system.
{% endhint %}

**Underlying Euler vaults** (borrowable USDC vaults for VBILL and STAC)

* **Governor Admin:** [Curator Safe](https://app.safe.global/transactions/history?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F), with a Permissions Layer for agents (2/5)\
  `0x1572063377a9a4f8065BD7bA0D7fa135cd13051F`\
  \
  The Curator Safe also acts as Governor of the [Oracle Router](https://create.euler.finance/oracle/0xDA8A71eAb4284C71Be969D4A5de7CdDE60d2b325) used by these markets, aligning vault allocation governance with oracle configuration.

**Securitize collateral vaults** (Securitize-deployed [ERC4626 wrappers](https://github.com/euler-xyz/evk-periphery/blob/master/src/Vault/deployed/ERC4626EVCCollateralSecuritize.sol) of VBILL, STAC, and other DS Tokens, used as collateral in the borrowable USDC markets above)

* **Governor:** Securitize, acting as the designated Transfer Agent for the underlying DS Tokens. These vaults enforce Securitize compliance (whitelisting, jurisdictional rules) at the contract level on every deposit, transfer, and seize; liquidators must also be whitelisted to receive collateral. See Securitize's [Euler integrates DS Protocol for curated lending](https://securitize.io/learn/blog/euler-integrates-ds-protocol-curated-lending) for the issuer-side description of the integration.
* KPK has no admin role on the collateral vaults. KPK's governance covers only the Earn vault and the borrowable USDC markets that lend against them.

#### Agent permissions

* **Rebalance agent:** may call `reallocate()` to move funds between approved markets.
* **Exit agent:** may call `reallocate()` to move funds to idle and `submitCap()` as part of Earn-level de-risking.
* **Pause agent:** may call `setCaps()` on the KPK-curated USDC markets to reduce or remove exposure, including pausing them outright.

Agents are deterministic programs, not AI. They can **only** call the whitelisted functions above. The vault is non-custodial.

For role definitions, see [Euler Earn roles](https://docs.euler.finance/user-guide/euler-earn).

</details>


# Change log

This page records **all major configuration and parameter changes** to KPK-curated Euler vaults.

Entries are listed in reverse chronological order. For current parameters, see the individual pool pages.

<table><thead><tr><th width="120.96875">Date</th><th width="124.5">Scope</th><th width="209.171875">Change</th><th width="217.24609375">Description/Rationale</th><th width="216.00390625">Tx/Reference</th></tr></thead><tbody><tr><td>2026-08-20</td><td>USDC Prime RWA</td><td>Accepted the eUSDC-137 (HINC) cap on the Earn vault and placed the market last in the withdrawal queue. Not yet in the supply queue.</td><td>Completes the three-day timelock opened on 17 August.</td><td><ul><li><a href="https://etherscan.io/tx/0xc6dc691a84825634e6bb80685673c2018a3260c0c9874314a203138261809f1c">acceptCap, updateWithdrawQueue</a></li></ul></td></tr><tr><td>2026-08-18</td><td>USDC Prime RWA</td><td>Enabled the HINC/USDC market (<code>eUSDC-137</code>). Deployed the RedStone oracle adapter, pointed the KPK router at HINC/USD and the ecHINC collateral vault, set LTV/LLTV to 66/74%, and transferred market governance to the Curator Safe.</td><td>Third Securitize market, tier C at a 50% allocation cap. Adds high-yield credit alongside the Treasury (VBILL) and AAA CLO (STAC) markets, at a materially higher borrow rate.</td><td><ul><li><a href="https://etherscan.io/tx/0x2db69ffd72639719f34990e0bff7e7f9d9493fd6bc44da0ddab59f108df512ff">oracle adapter deployment</a></li><li><a href="https://etherscan.io/tx/0x2c80e5947c8d0085721913ba29ac4d2db2821e407c345eb85557dba703df4884">govSetConfig</a></li><li><a href="https://etherscan.io/tx/0x852960553014734b02d1a7e867be01d2f3414ad422767a6ca99c5d3f72ced498">govSetResolvedVault</a></li><li><a href="https://etherscan.io/tx/0xcef6409edfc8506bb030f7537ce3316a19e75a438344c0d510d8f1efafdce43c">setLTV</a></li><li><a href="https://etherscan.io/tx/0x4326298706ae890d1e44485fb83e114076207445ed2a9700a64efffd2cb0ff1d">setGovernorAdmin</a></li></ul></td></tr><tr><td>2026-08-17</td><td>USDC Prime RWA</td><td>Deployed and parameterised the HINC/USDC market (<code>eUSDC-137</code>): $2m supply cap, $1.96m borrow cap, 10% max liquidation discount, 60-minute cool-off, 2.5% interest fee, IRM at 3.5% base and 5.0% at a 96% kink. No collateral enabled yet.</td><td>Prepares the third Securitize market for a prompt launch. The high kink and gentle post-kink slope suit collateral that cannot be unwound quickly, where a punitive rate would raise bad-debt risk rather than defend liquidity. The lower interest fee favours depositors.</td><td><ul><li><a href="https://etherscan.io/tx/0x849e5c530ae6658b57ee5abe6fcfeb64e74b4663604035a2c12a041a81e53476">market deployment</a></li><li><a href="https://etherscan.io/tx/0x6b4fc85cbffc9d9d293a26c16cb8c93c9e2c6d2c5d7bb8b9816f2fb808d36828">setCaps, setMaxLiquidationDiscount, setLiquidationCoolOffTime, setInterestFee, setFeeReceiver, setHookConfig</a></li><li><a href="https://etherscan.io/tx/0xf984bd020ef03e2144a94a737677626ef23e9cc1fd9d2e1deb1945cfe8c90a0e">IRM deployment</a></li><li><a href="https://etherscan.io/tx/0xf9d4ba91bbcbf8c2c90539ee4127d7a0ade66790247fe8bec3c904a6a2373a6a">setInterestRateModel, setInterestFee</a></li></ul></td></tr><tr><td>2026-08-11</td><td>USDC Prime RWA</td><td>Enabled the PYUSD/USDC market (<code>eUSDC-80</code>) as a second overflow market (Tier A, 65% cap). Supply queue reordered to PYUSD, RLUSD, Escrow; PYUSD inserted second in the withdraw queue.</td><td>Adds a second Sentora-curated overflow venue alongside RLUSD, so the rebalancing agent can move between them as borrow rates diverge while keeping a buffer that supports withdrawal liquidity. Supply order sends fresh deposits to the higher-yielding overflow market first.</td><td><ul><li><a href="https://etherscan.io/tx/0xe610a54ee528618d93dfeb7162c180e0f655cadac3e0ce334e16171049c6084e">acceptCap</a></li><li><a href="https://etherscan.io/tx/0xff27facd91b727559a30b2105c69d6fc2689e80c1c7c9fe51368a6120b4ea823">setSupplyQueue</a></li><li><a href="https://etherscan.io/tx/0xa7a11fdb0066cd9ba0c605324ce3353a3dfc175911f690ddc4777d89ecfab4cc">updateWithdrawQueue</a></li></ul></td></tr><tr><td>2026-05-22</td><td>USDC Prime RWA</td><td>Deprioritised the ACRED/USDC market (<code>eUSDC-107</code>): <code>setCaps</code> to 0, removed from withdraw queue, removed from supply queue. New withdraw queue: Escrow, Sentora RLUSD, STAC/USDC, VBILL/USDC. Supply queue limited to Sentora RLUSD then Escrow.</td><td>ACRED is not being onboarded at this point. Withdraw order drains the external Sentora RLUSD market first to preserve KPK-curated market depth for active borrowers. Supply queue excludes VBILL and STAC so fresh deposits don't compress their utilisation; allocation into those markets is handled by the rebalancer or manually.</td><td><ul><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F&#x26;id=multisig_0x1572063377a9a4f8065BD7bA0D7fa135cd13051F_0x169da7c5a64984a3f28ce34f6996884367e8ea3927c972ca60fb924d0b1360ae">setCaps to 0</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F&#x26;id=multisig_0x1572063377a9a4f8065BD7bA0D7fa135cd13051F_0x6c772a3ed243a8ae3a962bc01d55862f25460ea1b5f55f655cab2c118e2afda2">setWithdrawQueue</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F&#x26;id=multisig_0x1572063377a9a4f8065BD7bA0D7fa135cd13051F_0xc6440bfd35e223350d13689bb5d44e5fc93f99602dedc09859e044d8db8ec7e1">setSupplyQueue</a></li></ul></td></tr><tr><td>2026-05-19</td><td>Liquidator Safe</td><td>Added operator <code>0xAF150C6D108D0C2c96BbFc3CeF9B4848B5d99440</code> as Safe owner (threshold 2/5)</td><td>Completes the operator onboarding started on 2026-05-14: same signer was already added to the Euler curator (Governor Admin) Safe, so both Euler-related Safes now share the same 5-owner / 2-of-5 composition.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x856Dc3C271351587d22fba4C14CBE98530F7521c&#x26;id=multisig_0x856Dc3C271351587d22fba4C14CBE98530F7521c_0xef94881121bde7d0f0a19a25fde316667d7082876214fde43b14bf5ea36d174e">addOwnerWithThreshold</a></td></tr><tr><td>2026-05-14</td><td>USDC RWA</td><td>Onboard new operator address <code>0xAF150C6D108D0C2c96BbFc3CeF9B4848B5d99440</code> as Safe owner (threshold 2)</td><td>Part of the broader operator onboarding across the KPK curator stack. Adds the new signer to the Euler curator (Governor Admin) Safe.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F&#x26;id=multisig_0x1572063377a9a4f8065BD7bA0D7fa135cd13051F_0xc90bb92702cdb0a4971a902f20c838153f3d0ae210b41a0b45d545b99cf17bbb">addOwnerWithThreshold</a></td></tr><tr><td>2026-03-09</td><td>USDC RWA</td><td>Enabled RLUSD/USDC market (Tier A, 90% cap)</td><td>Added as a temporary allocation target to maintain baseline yield when utilisation in Securitize-linked markets (VBILL, STAC, ACRED) is insufficient.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x1572063377a9a4f8065BD7bA0D7fa135cd13051F&#x26;id=multisig_0x1572063377a9a4f8065BD7bA0D7fa135cd13051F_0x824d50adb49300ee9c694604b07d9df36566851fa06f7e1279cc8fdfd25ee0b7">Safe tx</a></td></tr></tbody></table>


# Morpho

KPK vaults on Morpho

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Prime Core</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fs16U3YjYmMtWGLUqtVaU%2FMorpho_USDC_Prime_L.png?alt=media&amp;token=18d26b0c-1c5c-425d-83b8-4472dd4e07f2">Morpho_USDC_Prime_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-prime-core">USDC Prime Core</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F1buzC2soiUWKQ4IgfuSr%2FMorpho_USDC_Prime_D.png?alt=media&amp;token=7fa0255a-993f-4807-8192-23dde9833ad8">Morpho_USDC_Prime_D.png</a></td></tr><tr><td><strong>KPK USDC Prime</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fs16U3YjYmMtWGLUqtVaU%2FMorpho_USDC_Prime_L.png?alt=media&amp;token=18d26b0c-1c5c-425d-83b8-4472dd4e07f2">Morpho_USDC_Prime_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-prime">USDC Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F1buzC2soiUWKQ4IgfuSr%2FMorpho_USDC_Prime_D.png?alt=media&amp;token=7fa0255a-993f-4807-8192-23dde9833ad8">Morpho_USDC_Prime_D.png</a></td></tr><tr><td><strong>KPK USDT Prime</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FeEkHpKN9CAhuBtGzGguj%2FMorpho_USDT_Prime_L.png?alt=media&amp;token=a3c30a8d-2d13-4c18-8ca6-99fedbd6a7f2">Morpho_USDT_Prime_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdt-prime">USDT Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fn6t8EUyNBXxWr6OiucfO%2FMorpho_USDT_Prime_D.png?alt=media&amp;token=e41703f6-d436-4f2b-bb4f-e7bdfc5818ef">Morpho_USDT_Prime_D.png</a></td></tr><tr><td><strong>KPK ETH Prime</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FqZIFGRljL5SbOwQFTNGt%2FMorpho_ETH_Prime_L.png?alt=media&amp;token=929323db-ecf9-4a9f-bf16-acc9a836614f">Morpho_ETH_Prime_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/eth-prime">ETH Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FBFJ9dvB4BkZrnNtPlcJ3%2FMorpho_ETH_Prime_D.png?alt=media&amp;token=08cf12d4-29de-478b-bab5-55e83dc226bf">Morpho_ETH_Prime_D.png</a></td></tr><tr><td><strong>KPK USDC Yield</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr><tr><td><strong>KPK USDC Yield RWA</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield-rwa">USDC Yield RWA</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr><tr><td><strong>KPK ETH Yield</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F9aJmMlwgNww5TzvEGJlm%2FMorpho_ETH_Yield_L.png?alt=media&amp;token=68132af5-4907-44d8-87a8-a007404148eb">Morpho_ETH_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/eth-yield">ETH Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FHycpPITG0Fg5ZTPsbWsk%2FMorpho_ETH_Yield_D.png?alt=media&amp;token=b55504c3-0d2b-493c-b09c-9f9a5824398b">Morpho_ETH_Yield_D.png</a></td></tr><tr><td><strong>KPK EURC Yield</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F8GjUKnAWsQqiGZf3rwvB%2FMorpho_EURC_Yield_L.png?alt=media&amp;token=b350bac1-51ba-42f0-b7af-d2f84122ed18">Morpho_EURC_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/eurc-yield">EURC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FP8RHzpysGUZYeedwQD44%2FMorpho_EURC_Yield_D.png?alt=media&amp;token=63fd8fb9-12b0-44fb-8196-69d2f2d5103e">Morpho_EURC_Yield_D.png</a></td></tr><tr><td><strong>KPK wARS Yield</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-98b553053c8f8294acfc537b91501bdaca2c85a1%2FMorpho_wARS_Yield_L.png?alt=media">Morpho_wARS_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/ethereum/wars-yield">wARS Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-07bdd3bd7920b06b07f78f55f3e66e652f901280%2FMorpho_wARS_Yield_D.png?alt=media">Morpho_wARS_Yield_D.png</a></td></tr><tr><td><strong>KPK USDC Yield</strong></td><td>Arbitrum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/arbitrum/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr><tr><td><strong>KPK USDC Yield</strong></td><td>Base</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="/vaults/vaults/morpho/base/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr></tbody></table>

### Protocol overview

[Morpho](https://morpho.org/) is a lending environment enabling ERC-4626 vaults that accept a single deposit asset and allocate it across underlying isolated lending markets. Depositors supply liquidity and earn lending yield; curators decide allocation eligibility and set safeguards.

{% hint style="success" %}
**Live metrics:** Track TVL, borrow APY, supply APY, utilisation and more for KPK Morpho vaults on Dune: <https://dune.com/kpk/kpk-morpho-vaults>
{% endhint %}

{% hint style="info" %}
**Vault architecture:** KPK curation runs on **Morpho v2 vaults**, which allocate directly into Morpho v1 markets via the [**Markets V1 Adapter**](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2). The v2 layer adds function-specific timelocks and supports MORPHO rewards. See [Vault Architecture](#vault-architecture).
{% endhint %}

### Curator model

Morpho uses a curator model: curators decide which lending markets a vault can allocate to and set safeguards. KPK acts as curator for selected Morpho vaults, defining safe operating conditions, selecting eligible markets, and reallocating liquidity when needed.\
\
**KPK’s curation focuses on three areas:** market selection, operational scalability through automation, and bespoke market deployments (where needed). All decisions follow the [Risk Framework](/vaults/infrastructure/risk-framework), which governs eligibility criteria, risk-tiering, and monitoring cadence.<br>

1. **Market selection and withdrawal reliability**\
   KPK sets market eligibility, exposure limits, and liquidity buffers. Only markets that pass [due diligence](/vaults/infrastructure/risk-framework) are enabled. Each approved market receives a **risk tier** and a **per-market supply cap** to limit concentration. Vaults maintain idle buffers designed to support smooth withdrawals.
2. <mark style="color:$primary;">**Real-time automation (agents) and monitoring**</mark>\ <mark style="color:$primary;">Vaults are monitored continuously by agents operated by KPK. They are deterministic programs that execute</mark> <mark style="color:$primary;">**whitelisted functions**</mark> <mark style="color:$primary;">through KPK’s</mark> [<mark style="color:$primary;">Permissions Layer</mark>](https://kpk.io/blog/permissions-layer)<mark style="color:$primary;">. Agents monitor utilisation, liquidity depth, APY shifts, oracle health, and price divergence versus reference venues. When conditions change, they can reduce or disable exposure to a market, increase idle funds, or rebalance across enabled markets within predefined limits.</mark>
   * <mark style="color:$primary;">**Rebalancing agent:**</mark> <mark style="color:$primary;">improves capital efficiency by reallocating across approved markets using a rules-based approach (e.g. tier- and cap-aware water-filling), subject to liquidity and safety checks.</mark>
   * <mark style="color:$primary;">**Exit agent:**</mark> <mark style="color:$primary;">responds to risk alerts (e.g. oracle staleness or divergence) within seconds by reducing or disabling exposure, increasing withdrawable liquidity, and prioritising safe exits within predefined limits.</mark>\
     \ <mark style="color:$primary;">This combination delivers fast incident response and steady, rules-driven allocation. For the technical design and allowed actions, see</mark> [<mark style="color:$primary;">Automation</mark>](/vaults/infrastructure/automation)<mark style="color:$primary;">.</mark>
3. **Custom market deployments**\
   Where needed, KPK works with counterparties (e.g. asset issuers or interested borrowers) to deploy bespoke markets and terms under the same risk framework.

### Vault architecture

KPK curation is delivered on **Morpho v2 vaults**, which sit at the top of the stack and route deposits into individual Morpho v1 markets through a single configured adapter:

* **v2 vaults** are the canonical, curated product. Each holds depositor assets, enforces function-specific timelocks, supports MORPHO rewards, and exposes one or more **adapters** that define which allocation targets the vault can use.
* **Markets V1 Adapter** is the adapter currently configured on every KPK v2 vault. It allocates **directly into individual Morpho v1 markets**, the venues where the underlying lending liquidity is held. There is no intermediate v1 MetaMorpho vault in the path: the v2 vault holds positions in v1 markets directly.
* **v1 (MetaMorpho) vaults** were the legacy curated layer. KPK wound down V1 curation at the end of May 2026, and all KPK curation now runs on v2 vaults. The v1 vaults remain onchain and a small number of depositors still hold positions in them, but they are no longer actively curated or rebalanced. New deposits should go to the v2 vaults.

{% hint style="info" %}
**Why this design:** v2 separates curation policy from allocation venue. Timelocks, role hierarchy, and reward routing live at the v2 layer; the Markets V1 Adapter is one allocation venue and can be replaced or supplemented by v2-native adapters in the future without touching depositor positions or vault governance.
{% endhint %}

### Liquidity and `forceDeallocate`

Withdrawals on Morpho v2 vaults are served first from the vault's idle balance, then from the **liquidity adapter**, a per-vault setting that designates a **single market** as the withdrawal venue. The liquidity adapter is configured on top of the Markets V1 Adapter and points at one of the v1 markets the adapter holds positions in. Withdrawals beyond idle pull from that one market until either the request is filled or that market's available liquidity is exhausted.

This is a structural difference from the previous Vault V1 Adapter setup, where the v2 vault sat above a v1 MetaMorpho vault that could draw from any of its enabled markets in a single transaction. With the Markets V1 Adapter, KPK selects which market backs withdrawals at any given time; the design favours allocator control over default withdrawal breadth. KPK aims to keep the liquidity adapter pointed at the market with the largest combination of vault exposure and underlying available liquidity, and is building monitoring to support more dynamic rotation.

`forceDeallocate` covers the remaining case: a withdrawal large enough to exhaust both idle and the liquidity adapter. In practice only very large exits reach this path. A depositor can call `forceDeallocate` on the v2 vault to pull liquidity from any other market in the adapter back to idle; Morpho's frontend uses it automatically, stitching a single-transaction withdrawal across the full set of listed markets. The call burns vault shares worth a fixed fraction of the amount deallocated, the **`forceDeallocate` penalty**, set per-adapter; the burned value remains in the vault and accrues to remaining depositors. KPK's standard value is **0.01%** across all vaults. The penalty applies only to forced deallocations: withdrawals served from idle or the liquidity adapter incur no fee, and a large exit can be split into smaller withdrawals served through the normal path, as the rebalancing agent replenishes the liquidity adapter between withdrawals.

The non-zero penalty is a deliberate curation choice. Because the penalty is charged against the caller's own shares (or shares of an address that has approved the caller), a non-zero value is restricted `forceDeallocate` to vault depositors and contracts acting with their approval. At the protocol default of zero, the call is fully permissionless and third parties can rearrange a vault's allocations at no cost. KPK's monitoring agent tracks deallocation activity; upon detecting a pattern of fragmented allocations against the curator's policy, it raises the penalty to **0.5%** and triggers an immediate rebalance to restore allocations. See [Automation](/vaults/infrastructure/automation#monitoring-agent) for details. The penalty in force on each vault is listed on that vault's page.

### Governance and control

Critical actions follow a layered process designed for transparency, security, and timely response.

{% tabs %}
{% tab title="Governance changes (timelocked)" %}
**v2 vault (curated product):** changes that can expand permissions or increase risk (e.g., role updates, enabling a new market, raising allocation ceilings, adding or removing an adapter, or changing risk parameters) execute via KPK multisigs under **function-specific timelocks** (KPK’s 3/7/14-day schedule, detailed under Key settings and roles below). The Guardian can intervene during the timelock window if required.

**v1 vaults (legacy):** KPK wound down v1 curation at the end of May 2026. The vaults remain onchain and still hold a small number of depositor positions, but KPK no longer rebalances them or makes governance changes to them.
{% endtab %}

{% tab title="Operational changes (not timelocked)" %}
Operational actions within pre-approved bounds, such as `reallocate()` between enabled markets, increasing the idle buffer, or setting a market cap to zero, execute under scoped roles and are not timelocked, allowing agents to respond within seconds.
{% endtab %}
{% endtabs %}

**Key settings and roles**

The following roles and settings apply across all KPK vaults:

* **Owner and sentinel:** [Security Council Safe](https://app.safe.global/transactions/history?safe=eth:0x354C92aF243d53A24feb3dFF20372Af7b7c47478) (5/8)\
  `0x354C92aF243d53A24feb3dFF20372Af7b7c47478`
* **Curator and allocator:** Curator Safe, with a Permissions Layer for agents (2/5). As well as the KPK Deployer 1 (`0xF0Cf1e3Ec6264b03241826f5b2aBda17B4352A75`), KPK Deployer 2 (`0x331D9C769185AE25233314859d53c2b9203a1204`) and KPK Deployer 3 (`0xAF150c6d108D0C2c96BbFc3CeF9B4848b5D99440`) holding Allocator roles for emergency scenarios. Visit each vault page for the specific address.
* **Timelocks:**
  * **3 days:** add adapter; increase absolute or relative cap
  * **7 days:** remove adapter, update gates, increase timelock
  * **14 days:** adjust management or performance fees

KPK Morpho v2 vaults apply function-specific timelocks to wrapper configuration (adapter, fee, and risk-parameter changes), and are controlled by the same KPK Safes that governed the corresponding legacy v1 vaults.

For detailed configuration, see [Morpho v2 Roles and Timelocks](https://docs.morpho.org/curate/concepts/roles/#morpho-vaults-v2-roles), the [Changelog page](https://docs.kpk.io/changelog/), and the individual vault pages. For protocol-level context on Morpho v2 versus v1, see Morpho [documentation](https://docs.morpho.org/learn/concepts/vault-v2/#key-feature-comparison-morpho-vaults-v1-vs-morpho-vaults-v2).

{% hint style="info" %}
**Allocator usage:** KPK does not use Morpho's public Allocator role, reducing the attack surface. Automation is instead scoped through KPK’s Permissions Layer and Morpho’s roles system.
{% endhint %}

### Vault configuration

Enabled markets, caps, risk tiers, oracles, allocation rules, and governance addresses are documented on each vault page and kept up to date in the [Changelog page](https://docs.kpk.io/changelog/).&#x20;

* [Ethereum](/vaults/vaults/morpho/ethereum)
  * [KPK USDC Prime Core](/vaults/vaults/morpho/ethereum/usdc-prime-core)
  * [KPK USDC Prime](/vaults/vaults/morpho/ethereum/usdc-prime)
  * [KPK USDT Prime](/vaults/vaults/morpho/ethereum/usdt-prime)
  * [KPK ETH Prime](/vaults/vaults/morpho/ethereum/eth-prime)
  * [KPK USDC Yield](/vaults/vaults/morpho/ethereum/usdc-yield)
  * [KPK USDC Yield RWA](/vaults/vaults/morpho/ethereum/usdc-yield-rwa)
  * [KPK ETH Yield](/vaults/vaults/morpho/ethereum/eth-yield)
  * [KPK EURC Yield](/vaults/vaults/morpho/ethereum/eurc-yield)
  * [KPK wARS Yield](/vaults/vaults/morpho/ethereum/wars-yield)
* [Arbitrum](/vaults/vaults/morpho/arbitrum)
  * [KPK USDC Yield](/vaults/vaults/morpho/arbitrum/usdc-yield)


# Ethereum

KPK-curated Morpho vaults on Ethereum.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Prime Core</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/usdc-prime-core">USDC Prime Core</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fs16U3YjYmMtWGLUqtVaU%2FMorpho_USDC_Prime_L.png?alt=media&amp;token=18d26b0c-1c5c-425d-83b8-4472dd4e07f2">Morpho_USDC_Prime_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F1buzC2soiUWKQ4IgfuSr%2FMorpho_USDC_Prime_D.png?alt=media&amp;token=7fa0255a-993f-4807-8192-23dde9833ad8">Morpho_USDC_Prime_D.png</a></td></tr><tr><td><strong>KPK USDC Prime</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/usdc-prime">USDC Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fs16U3YjYmMtWGLUqtVaU%2FMorpho_USDC_Prime_L.png?alt=media&amp;token=18d26b0c-1c5c-425d-83b8-4472dd4e07f2">Morpho_USDC_Prime_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F1buzC2soiUWKQ4IgfuSr%2FMorpho_USDC_Prime_D.png?alt=media&amp;token=7fa0255a-993f-4807-8192-23dde9833ad8">Morpho_USDC_Prime_D.png</a></td></tr><tr><td><strong>KPK USDT Prime</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/usdt-prime">USDT Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FeEkHpKN9CAhuBtGzGguj%2FMorpho_USDT_Prime_L.png?alt=media&amp;token=a3c30a8d-2d13-4c18-8ca6-99fedbd6a7f2">Morpho_USDT_Prime_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fn6t8EUyNBXxWr6OiucfO%2FMorpho_USDT_Prime_D.png?alt=media&amp;token=e41703f6-d436-4f2b-bb4f-e7bdfc5818ef">Morpho_USDT_Prime_D.png</a></td></tr><tr><td><strong>KPK ETH Prime</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/eth-prime">ETH Prime</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FqZIFGRljL5SbOwQFTNGt%2FMorpho_ETH_Prime_L.png?alt=media&amp;token=929323db-ecf9-4a9f-bf16-acc9a836614f">Morpho_ETH_Prime_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FBFJ9dvB4BkZrnNtPlcJ3%2FMorpho_ETH_Prime_D.png?alt=media&amp;token=08cf12d4-29de-478b-bab5-55e83dc226bf">Morpho_ETH_Prime_D.png</a></td></tr><tr><td><strong>KPK USDC Yield</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr><tr><td><strong>KPK USDC Yield RWA</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield-rwa">USDC Yield RWA</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr><tr><td><strong>KPK ETH Yield</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/eth-yield">ETH Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F9aJmMlwgNww5TzvEGJlm%2FMorpho_ETH_Yield_L.png?alt=media&amp;token=68132af5-4907-44d8-87a8-a007404148eb">Morpho_ETH_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FHycpPITG0Fg5ZTPsbWsk%2FMorpho_ETH_Yield_D.png?alt=media&amp;token=b55504c3-0d2b-493c-b09c-9f9a5824398b">Morpho_ETH_Yield_D.png</a></td></tr><tr><td><strong>KPK EURC Yield</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/eurc-yield">EURC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F8GjUKnAWsQqiGZf3rwvB%2FMorpho_EURC_Yield_L.png?alt=media&amp;token=b350bac1-51ba-42f0-b7af-d2f84122ed18">Morpho_EURC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FP8RHzpysGUZYeedwQD44%2FMorpho_EURC_Yield_D.png?alt=media&amp;token=63fd8fb9-12b0-44fb-8196-69d2f2d5103e">Morpho_EURC_Yield_D.png</a></td></tr><tr><td><strong>KPK wARS Yield</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/morpho/ethereum/wars-yield">wARS Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-98b553053c8f8294acfc537b91501bdaca2c85a1%2FMorpho_wARS_Yield_L.png?alt=media">Morpho_wARS_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2Fgit-blob-07bdd3bd7920b06b07f78f55f3e66e652f901280%2FMorpho_wARS_Yield_D.png?alt=media">Morpho_wARS_Yield_D.png</a></td></tr></tbody></table>


# ETH Prime

The KPK ETH Prime vault allocates across selected blue-chip collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to **low-risk ETH yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-eth-mainnet">https://app.kpk.io/vaults/morpho-eth-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-eth-prime-vault">https://dune.com/kpk/kpk-eth-prime-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="#key-information">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>WETH</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0xBb50A5341368751024ddf33385BA8cf61fE65FF9#code">0xBb50A5341368751024ddf33385BA8cf61fE65FF9</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>75-100%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies ETH to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets**, with enforced caps and buffers to support **instant withdrawable liquidity** for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

#### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="158.15625">Market exposure</th><th width="102.7578125">Issuer</th><th width="103.22265625">Risk tier</th><th width="140.484375" align="right">Allocation cap</th><th align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb8fc70e82bc5bb53e773626fcc6a23f7eefa036918d7ef216ecfb1950a94a85e/wsteth-weth">wstETH/WETH</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">96.5%</td><td>Morpho Labs</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xd0e50cdac92fe2172043f5e0c36532c6369d24947e40968f34a5e8819ca9ec5d/wsteth-weth">wstETH/WETH</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">94.5%</td><td>Morpho Labs</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x37e7484d642d90f14451f1910ba4b7b8e4c3ccdd0ec28f8b2bdb35479e472ba7/weeth-weth">weETH/WETH</a></td><td>EtherFi</td><td>B</td><td align="right">50%</td><td align="right">94.5%</td><td>EtherFi</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x251b7cbc2c33ba4eabe3b7163ad0fdd3cfb97de80d8377f0f48b8d31aee7606f/reth-weth">rETH/WETH</a></td><td>Rocketpool</td><td>B</td><td align="right">50%</td><td align="right">94.5%</td><td>Morpho Labs</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa) (2/5), with a Permissions Layer for agents, is\
`0xC266b1181A80E84EDC2c6596718E88E8115c1eaa`.


# ETH Yield

The KPK ETH Yield vault allocates across selected interest-bearing collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to a **higher ETH yield** than [KPK ETH Prime](/vaults/vaults/morpho/ethereum/eth-prime).

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-eth-yield-mainnet">https://app.kpk.io/vaults/morpho-eth-yield-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-eth-yield-vault">https://dune.com/kpk/kpk-eth-yield-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>WETH</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x5dbf760b4fd0cDdDe0366b33aEb338b2A6d77725#code">0x5dbf760b4fd0cDdDe0366b33aEb338b2A6d77725</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥50%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies ETH to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets a balanced risk profile across **incentive-rich collateral markets,** with enforced caps and buffers to support withdrawal liquidity for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="168.8359375">Market exposure</th><th width="108.640625">Issuer</th><th width="104.0234375">Risk tier</th><th width="143.88671875" align="right">Allocation cap</th><th width="83" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb8fc70e82bc5bb53e773626fcc6a23f7eefa036918d7ef216ecfb1950a94a85e/wsteth-weth">wstETH/WETH</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">96.5%</td><td>Morpho Labs</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x37e7484d642d90f14451f1910ba4b7b8e4c3ccdd0ec28f8b2bdb35479e472ba7/weeth-weth">weETH/WETH</a></td><td>Etherfi</td><td>B</td><td align="right">90%</td><td align="right">94.5%</td><td>Etherfi</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xd98cd88ae5b336086b39fb1d62ba6171282e946105b010143f0e89f8fe7cff36/saveth-weth?subTab=yourPosition">savETH/WETH</a></td><td>Avant</td><td>C</td><td align="right">75%</td><td align="right">91.5%</td><td>Avant</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x8bfd8f4f146c660f201a34b7a05d5eade3dc95b7b329210703855b60ad871a24/teth-weth">tETH/WETH</a></td><td>Treehouse</td><td>C</td><td align="right">30%</td><td align="right">91.5%</td><td>Treehouse</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075) (2/5), with a Permissions Layer for agents, is\
`0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075`.


# EURC Yield

The KPK EURC Yield vault allocates across selected blue-chip collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to **low-risk EURC yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-eurc-mainnet">https://app.kpk.io/vaults/morpho-eurc-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-eurc-yield-vault">https://dune.com/kpk/kpk-eurc-yield-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="#key-information">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>EURC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0xa877D5bb0274dcCbA8556154A30E1Ca4021a275f#code">0xa877D5bb0274dcCbA8556154A30E1Ca4021a275f</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥30%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies EURC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets**, with enforced caps and buffers to support **instant withdrawable liquidity** for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="160.125">Market exposure</th><th width="104.5859375">Issuer</th><th width="111.0078125">Risk tier</th><th width="143.23828125" align="right">Allocation cap</th><th align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x7421c2741e064e8c53fcb5de9faf7f0025dce75bc1caf26774dd878291c81dac/wsteth-eurc">wstETH/EURC</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xff527fe9c6516f9d82a3d51422ccb031d123266e6e26d4c22c942a948c180a75/wbtc-eurc">WBTC/EURC</a></td><td>BitGo</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x7384bd82fb2f2a562555d4aab25583b4e40deed124b7c95dc16be34547434193/weth-eurc">WETH/EURC</a></td><td>–</td><td>A</td><td align="right">20%</td><td align="right">86%</td><td>Chainlink</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5) (2/5), with a Permissions Layer for agents, is\
`0xd15f11B334e1e233127302E5F759C17DA1260df5`.


# USDC Prime

The KPK USDC Prime vault allocates across selected blue-chip collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to **low-risk USDC yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-usdc-mainnet">https://app.kpk.io/vaults/morpho-usdc-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-prime-vault">https://dune.com/kpk/kpk-usdc-prime-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="/vaults">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x4Ef53d2cAa51C447fdFEEedee8F07FD1962C9ee6#code">0x4Ef53d2cAa51C447fdFEEedee8F07FD1962C9ee6</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>75-100%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets**, with enforced caps and buffers to support **instant withdrawable liquidity** for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="163.85546875">Market exposure</th><th width="111.34765625">Issuer</th><th width="100.296875">Risk tier</th><th width="140.05078125" align="right">Allocation cap</th><th width="93.34765625" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x94b823e6bd8ea533b4e33fbc307faea0b307301bc48763acc4d4aa4def7636cd/weth-usdc">WETH/USDC</a></td><td>Ethereum</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x3a85e619751152991742810df6ec69ce473daef99e28a64ab2340d7b7ccfee49/wbtc-usdc">WBTC/USDC</a></td><td>BitGo</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc/wsteth-usdc">wstETH/USDC</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64/cbbtc-usdc">cbBTC/USDC</a></td><td>Coinbase</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x0a15460ad263c2186fe0b5df20a8cf71d55f3cfa06de15edcf6138f6b8edd8bf/reth-usdc#advanced">rETH/USDC</a></td><td>Rocketpool</td><td>B</td><td align="right">45%</td><td align="right">86%</td><td>Fundamental + Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb8fef900b383db2dbbf4458c7f46acf5b140f26d603a6d1829963f241b82510e/oeth-usdc">OETH/USDC</a></td><td>Origin</td><td>B</td><td align="right">30%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xfb7d54e0ce71efc8fffd3f4e1db0afa9265882da5cc76604b62adfac64501e80/usdc-lseth#market">LsETH/USDC</a></td><td>Liquid Collective</td><td>B</td><td align="right">10%</td><td align="right">86%</td><td>Fundamental + Chainlink</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE) (2/5), with a Permissions Layer for agents, is\
`0xf8182E5827c06a47A985eC565A3bcd56437a97bE`.


# USDC Prime Core

The KPK USDC Prime Core vault allocates across selected blue-chip collateral markets (i.e. Tier A only), optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. Exposure is capped per market, with **24/7 automation** and liquidity buffers to preserve smooth withdrawals and stable, risk-adjusted returns.

The vault provides exposure to **low-risk USDC yield**, with a stricter mandate than KPK USDC Prime: Tier A markets only.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-usdc-prime-core-mainnet">https://app.kpk.io/vaults/morpho-usdc-prime-core-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-prime-core-vault">https://dune.com/kpk/kpk-usdc-prime-core-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x1a1985F50352b58090eb36425AfdFacbaC7806F4#code">0x1a1985F50352b58090eb36425AfdFacbaC7806F4</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>90-100%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocate penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets restricted to Tier A**, with enforced caps and buffers to support instant withdrawable liquidity for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="163.85546875">Market exposure</th><th width="111.34765625">Issuer</th><th width="100.296875">Risk tier</th><th width="140.05078125" align="right">Allocation cap</th><th width="93.34765625" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x3a85e619751152991742810df6ec69ce473daef99e28a64ab2340d7b7ccfee49/wbtc-usdc">WBTC/USDC</a></td><td>BitGo</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc/wsteth-usdc">wstETH/USDC</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64/cbbtc-usdc">cbBTC/USDC</a></td><td>Coinbase</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E) (2/5), with a Permissions Layer for agents, is\
`0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E`.


# USDC Yield

The KPK USDC Yield vault allocates across selected interest-bearing collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to a **higher USDC yield** than [KPK USDC Prime](/vaults/vaults/morpho/ethereum/usdc-prime).

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-usdc-yield-mainnet">https://app.kpk.io/vaults/morpho-usdc-yield-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-yield-vault">https://dune.com/kpk/kpk-usdc-yield-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0xD5cCe260E7a755DDf0Fb9cdF06443d593AaeaA13#code">0xD5cCe260E7a755DDf0Fb9cdF06443d593AaeaA13</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥30%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets a balanced risk profile across **incentive-rich collateral markets,** with enforced caps and buffers to support withdrawal liquidity for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="168.8359375">Market exposure</th><th width="108.640625">Issuer</th><th width="104.0234375">Risk tier</th><th width="141.45703125" align="right">Allocation cap</th><th width="83" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x34377fc4f617c51818e92c79df31ff270c6a91bc94ad32e367fdf59b9f4ac5dd/weeth-usdc">weETH/USDC</a></td><td>EtherFi</td><td>B</td><td align="right">90%</td><td align="right">77%</td><td>Fundamental + Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xe07d416323a1afbfe0bf2fe27ffb549ff565cf5c86d21b79fc60664038e597c9/savusd-usdc">savUSD/USDC</a></td><td>Avant</td><td>C</td><td align="right">65%</td><td align="right">91.5%</td><td>Fundamental</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xbbf7ce1b40d32d3e3048f5cf27eeaa6de8cb27b80194690aab191a63381d8c99/siusd-usdc">siUSD/USDC</a></td><td>InfiniFi</td><td>C</td><td align="right">60%</td><td align="right">91.5%</td><td>InfiniFi</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x88ab06d43b089e574bbe312deee6cdb292702c3a2e765059492c34b1cd298acf/ftusd-usdc">ftUSD/USDC</a></td><td>Flying Tulip</td><td>C</td><td align="right">30%</td><td align="right">86%</td><td>Fundamental + Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xacc49fbf58feb1ac971acce68f8adc177c43682d6a7087bbd4991a05cb7a2c67/srroyusdc-usdc">srRoyUSDC</a></td><td>Royco</td><td>D</td><td align="right">20%</td><td align="right">91.5%</td><td>Royco</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171) (2/5), with a Permissions Layer for agents, is\
`0x7E43Df1c1c5A2245858b60D4655fDA83704e4171`.


# USDC Yield RWA

The KPK USDC Yield RWA vault allocates across selected real-world-asset collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> lends against **KPK-vetted real-world assets**, a distinct risk profile from KPK's crypto-collateral vaults.

Depositors supply USDC and earn the borrow rate paid by borrowers who post tokenised real-world assets as collateral. The vault does not hold those assets, so depositors take credit and liquidation risk on them rather than price exposure to them.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-usdc-yield-rwa-mainnet">https://app.kpk.io/vaults/morpho-usdc-yield-rwa-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-yield-rwa-vault">https://dune.com/kpk/kpk-usdc-yield-rwa-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x7a72bcD2c3F7F7e4D6679170a0625bAB15D7DDa1#code">0x7a72bcD2c3F7F7e4D6679170a0625bAB15D7DDa1</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥50%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK's [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **markets collateralised by tokenised real-world assets**, principally private credit and tokenised funds, with enforced caps and buffers to support withdrawal liquidity for depositors.

KPK allocates to markets that already have other curators supplying alongside us, rather than creating its own RWA markets. Real-world-asset collateral is redeemable only through its issuer, on the issuer's schedule, so a supplier's practical exit is the market's free liquidity rather than a sale of the collateral. Supplying a shared market preserves the option to withdraw before other suppliers if an underlying asset deteriorates; a market where KPK were the sole supplier would remove that option.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK's [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

Real-world asset markets differ from the crypto-collateral markets in KPK's other vaults in three ways that shape how this vault is run:

* **Collateral prices are reported, not traded.** Each market's oracle reads a net asset value published by the asset's issuer or administrator, not a market price from a trading venue. A price that only moves when the issuer posts a new value cannot reflect deterioration between prints.
* **Liquidation depends on redemption, not resale.** Tokenised credit and fund collateral have little or no secondary market, and holders are often restricted to permissioned addresses. A liquidator's exit is issuer redemption on the issuer's timetable, so liquidations can clear slowly or not at all.
* **Exits are bounded by market liquidity.** Because the collateral cannot be sold quickly, a supplier's withdrawal capacity is the borrowers' repayments plus any unborrowed liquidity, not the size of the collateral pool.

Market selection is set against these constraints. Allocation caps are sized so that a full loss in any single market is absorbable, and are set per market rather than by tier by default.

#### Risk-tier snapshot

<table><thead><tr><th width="180">Market exposure</th><th width="110">Issuer</th><th width="100">Risk tier</th><th width="140" align="right">Allocation cap</th><th width="90" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0xe83d72fa5b00dcd46d9e0e860d95aa540d5ec106da5833108a9f826f21f36f52/aa-falconxusdc-usdc">AA_FalconXUSDC/USDC</a></td><td>Pareto/FalconX</td><td>C</td><td align="right">60%</td><td align="right">77%</td><td>Fundamental</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x2a0b893471c785a1f0fda00df7fca578b703eef7ae7186d2429c2824b5212467/wfalconx-usdc">wFalconX/USDC</a></td><td>3F/Pareto/FalconX</td><td>D</td><td align="right">40%</td><td align="right">91.5%</td><td>Fundamental</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xe3df58f9d3011b7481ff36b939fa5f8da642f34ea5792d25d3958dbf1efa26d7/usd3-usdc">USD3/USDC</a></td><td>3Jane</td><td>D</td><td align="right">20%</td><td align="right">91.5%</td><td>Fundamental</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xf8c5aa31ea6b2a068a9eddb46dd110cae57bf0f12be9583a3f9a818effecba89/pt-usd3-17dec2026-usdc">PT-USD3-17DEC2026/USDC</a></td><td>3Jane</td><td>D</td><td align="right">20%</td><td align="right">86%</td><td>Pendle fixed discount</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xef2c308b5abecf5c8750a1aa82b47c558005feb7a03f4f8e1ad682d71ac8d0ba/mf-one-usdc">mF-ONE/USDC</a></td><td>Midas/Fasanara</td><td>D</td><td align="right">20%</td><td align="right">91.5%</td><td>Fundamental</td></tr></tbody></table>

#### Key risks

* Credit risk on the underlying real-world assets, including borrower default, servicer failure, and originator concentration within a single asset
* Liquidation risk where collateral has no secondary market and is redeemable only through its issuer, so a liquidation may not clear at the oracle price or within a predictable period
* Oracle risk on issuer-reported net asset values, including stale prints, an absence of automatic downward revaluation, and issuer discretion over when a decline is recognised
* Liquidity and utilisation risk in underlying markets affecting withdrawal latency, with exits bounded by market liquidity rather than collateral value
* Concentration risk where separate markets share an underlying asset, issuer, or oracle, so exposures that appear diversified can move together
* Counterparty and legal risk on issuers, administrators, and the structures holding the underlying assets, including bankruptcy remoteness and redemption enforceability
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4) (2/5), with a Permissions Layer for agents, is\
`0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4`.


# USDT Prime

The KPK USDT Prime vault allocates across selected blue-chip collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to **low-risk USDT yield**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><mark style="color:$primary;">Deposit</mark></td><td><a href="https://app.kpk.io/vaults/morpho-usdt-prime-mainnet">https://app.kpk.io/vaults/morpho-usdt-prime-mainnet</a></td></tr><tr><td><mark style="color:$primary;">Live metrics</mark></td><td><a href="https://dune.com/kpk/kpk-usdt-prime-vault">https://dune.com/kpk/kpk-usdt-prime-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDT</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x870F0BF29A25A40E7CC087cD5C53e70C11F2C8A8">0x870F0BF29A25A40E7CC087cD5C53e70C11F2C8A8</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>75-100%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDT to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets**, with enforced caps and buffers to support **instant withdrawable liquidity** for depositors.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).&#x20;

#### Risk-tier snapshot

<table><thead><tr><th width="158.8359375">Market exposure</th><th width="108.640625">Issuer</th><th width="101.0234375">Risk tier</th><th width="140.7265625" align="right">Allocation cap</th><th width="94.046875" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x45671fb8d5dea1c4fbca0b8548ad742f6643300eeb8dbd34ad64a658b2b05bca/cbbtc-usdt"><mark style="color:$primary;">cbBTC/USDT</mark></a></td><td>Coinbase</td><td><mark style="color:$primary;">A</mark></td><td align="right"><mark style="color:$primary;">90%</mark></td><td align="right"><mark style="color:$primary;">86%</mark></td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xa921ef34e2fc7a27ccc50ae7e4b154e16c9799d3387076c421423ef52ac4df99/wbtc-usdt"><mark style="color:$primary;">WBTC/USDT</mark></a></td><td>BitGo</td><td><mark style="color:$primary;">A</mark></td><td align="right"><mark style="color:$primary;">90%</mark></td><td align="right"><mark style="color:$primary;">86%</mark></td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xe7e9694b754c4d4f7e21faf7223f6fa71abaeb10296a4c43a54a7977149687d2/wsteth-usdt"><mark style="color:$primary;">wstETH/USDT</mark></a></td><td>Lido</td><td><mark style="color:$primary;">A</mark></td><td align="right"><mark style="color:$primary;">90%</mark></td><td align="right"><mark style="color:$primary;">86%</mark></td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x3274643db77a064abd3bc851de77556a4ad2e2f502f4f0c80845fa8f909ecf0b/susds-usdt">sUSDS/USDT</a></td><td>Sky</td><td>B</td><td align="right">40%</td><td align="right">96.5%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xb7843fe78e7e7fd3106a1b939645367967d1f986c2e45edb8932ad1896450877/xaut-usdt"><mark style="color:$primary;">XAUt/USDT</mark></a></td><td>Tether</td><td><mark style="color:$primary;">B</mark></td><td align="right"><mark style="color:$primary;">60%</mark></td><td align="right"><mark style="color:$primary;">77%</mark></td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xc2c53d2b868e163da71de14a5113cc2743fc9b5ad7488334720ed2846566a8f6/weeth-usdt">weETH/USDT</a></td><td>EtherFi</td><td>B</td><td align="right">40%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0xa4774e3e693fff2ebd1dcbbd69b1b0a5b9bb0ccc753bfda5dd07bdac97c4818a/syrupusdt-usdt">syrupUSDT/USDT</a></td><td>Maple</td><td>B</td><td align="right">40%</td><td align="right">91.5%</td><td>Maple</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8) (2/5), with a Permissions Layer for agents, is\
`0x834e1c1ea40173B82106F9177646b66d96AE7De8`.


# wARS Yield

The KPK wARS Yield vault allocates across selected collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to **ARS-denominated yield**, KPK’s first vault on a wFIAT stablecoin.

wARS is part of [wFIAT](https://action.ripio.com/en/blog/chainlink-and-ripio-launch-wars-usd-and-wbrl-usd-price-feeds-bringing-latin-american-currencies-to-defi), Ripio’s suite of fully collateralised local-currency stablecoins pegged 1:1 to their respective fiat currencies. Each wARS is backed by Argentine pesos held in reserve at regulated financial institutions and is minted and redeemed through Ripio, a Latin American crypto platform that has operated since 2013.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-wars-yield-mainnet">https://app.kpk.io/vaults/morpho-wars-yield-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-wars-yield-vault">https://dune.com/kpk/kpk-wars-yield-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>wARS</td></tr><tr><td><strong>Address</strong></td><td>0x</td></tr><tr><td><strong>Liquidity target</strong></td><td><a href="https://etherscan.io/address/0xb5ce3CA2C774b72955C25875022FdD91f7a7B938#code">0xb5ce3CA2C774b72955C25875022FdD91f7a7B938</a></td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies wARS to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets **blue-chip collateral markets**, with enforced caps and buffers to support withdrawal liquidity for depositors. wARS is the loan asset rather than the collateral: WBTC, WETH, USDC and USDT holders borrow wARS, and depositors earn the rate those borrowers pay.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="160.125">Market exposure</th><th width="104.5859375">Issuer</th><th width="111.0078125">Risk tier</th><th width="143.23828125" align="right">Allocation cap</th><th align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/ethereum/variable/0x7730ef5352412a37d441ab960f5c8f33ea8d1c9f01c34fc2340f65fa1c59ce69/wbtc-wars">WBTC/wARS</a></td><td>BitGo</td><td>A</td><td align="right">90%</td><td align="right">62.5%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x9666c9b61cc58e3187c6e344697ed31fe558dc01809740417beee07dc77d96b3/weth-wars">WETH/wARS</a></td><td>—</td><td>A</td><td align="right">90%</td><td align="right">62.5%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x7bb3c45550c02e7e00ddbf2f847717dc4485df4bbae3b992178a74f0d45c4e87/usdc-wars">USDC/wARS</a></td><td>Circle</td><td>A</td><td align="right">90%</td><td align="right">77%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/ethereum/variable/0x8e6271e428ee5b41c98a3765919e359f16b0c6261c85d84f9b9b8b5242ab9c9e/usdt-wars">USDT/wARS</a></td><td>Tether</td><td>A</td><td align="right">90%</td><td align="right">77%</td><td>Chainlink</td></tr></tbody></table>

#### Key risks

* Thinner onchain liquidity for wARS than for KPK’s other deposit assets, which can widen slippage for liquidators converting seized collateral and slow how quickly a liquidation clears
* Currency risk on ARS, including devaluation, elevated volatility, and the effect of Argentine capital controls on convertibility
* Oracle risk on the [Chainlink USD/ARS feed](https://etherscan.io/address/0xBb65fa58BDb7d33e4a3D1A40a7A9BD99E746367b), which references a crypto-derived rate rather than an official or onshore rate and has a short onchain history. Every enabled market prices its wARS leg off this one feed, so a divergence moves effective collateralisation across all of them together
* Issuer risk on Ripio, including reserve management and redemption availability for wARS
* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/settings/setup?safe=eth:0xEaCFA855d172F1B40D9b04245960b2c12266C5f5) (2/5), with a Permissions Layer for agents, is\
`0xEaCFA855d172F1B40D9b04245960b2c12266C5f5`.


# Arbitrum

KPK-curated Morpho vaults on Arbitrum.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Yield</strong></td><td>Arbitrum</td><td><a href="/vaults/vaults/morpho/arbitrum/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr></tbody></table>


# USDC Yield

The KPK USDC Yield vault allocates across selected interest-bearing collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to a **higher USDC yield on Arbitrum** than Prime alternatives.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/morpho-usdc-yield-arbitrum">https://app.kpk.io/vaults/morpho-usdc-yield-arbitrum</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-yield-arbitrum-vault">https://dune.com/kpk/kpk-usdc-yield-arbitrum-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Arbitrum</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://arbiscan.io/address/0x5837e4189819637853a357aF36650902347F5e73">0x5837e4189819637853a357aF36650902347F5e73</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥30%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

This vault targets a **broader, incentive-rich market set** on Arbitrum, while maintaining enforced caps and buffers to support **instant withdrawable liquidity** for depositors.

Vault management is **fully automated** through three dedicated agents operated by KPK (see [Automation Layer](/vaults/infrastructure/automation)): a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence versus reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yield, alongside depositor and third-party activity against the vault itself.

* The **rebalancing agent** improves capital efficiency in normal conditions, allocating and rebalancing across approved markets using tier- and cap-aware rules, subject to safety checks.
* The **exit agent** responds to risk alerts (e.g., oracle staleness, liquidity stress, or severe price divergence) by reducing or disabling exposure, raising the idle buffer, and prioritising safe exits.

Both agents can act **within seconds** and strictly within **whitelisted permissions**.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="165.13671875">Market exposure</th><th width="106.0234375">Issuer</th><th width="113.6953125">Risk tier</th><th width="142.06640625" align="right">Allocation cap</th><th align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/arbitrum/variable/0xca83d02be579485cc10945c9597a6141e772f1cf0e0aa28d09a327b6cbd8642c/weth-usdc">WETH/USDC</a></td><td>—</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/arbitrum/variable/0x33e0c8ab132390822b07e5dc95033cf250c963153320b7ffca73220664da2ea0/wsteth-usdc">wstETH/USDC</a></td><td>Lido</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/arbitrum/variable/0xe6392ff19d10454b099d692b58c361ef93e31af34ed1ef78232e07c78fe99169/wbtc-usdc">WBTC/USDC</a></td><td>BitGo</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/arbitrum/variable/0xd09404e9512e1341321c8ae3bd663fab7087582142ac61486635a6c072c2af12/weeth-usdc">weETH/USDC</a></td><td>EtherFi</td><td>B</td><td align="right">40%</td><td align="right">86%</td><td>Redstone</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response.

The [Curator and allocator Safe](https://app.safe.global/home?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d) (2/5), with a Permissions Layer for agents, is\
`0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d`


# Base

KPK-curated Morpho vaults on Base.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC Yield</strong></td><td>Base</td><td><a href="/vaults/vaults/morpho/base/usdc-yield">USDC Yield</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr></tbody></table>


# USDC Yield

The KPK USDC Yield vault allocates across selected interest-bearing collateral markets, optimising risk-adjusted yield within isolated lending markets on Morpho. Yield comes from overcollateralised lending rates in underlying markets. <mark style="color:$primary;">Exposure is capped per market, with</mark> <mark style="color:$primary;">**24/7 automation**</mark> <mark style="color:$primary;">and</mark> <mark style="color:$primary;">**liquidity buffers**</mark> <mark style="color:$primary;">to preserve smooth withdrawals and stable, risk-adjusted returns.</mark>

<mark style="color:$primary;">The vault</mark> provides exposure to a **higher USDC yield on Base**.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.morpho.org/base/vault/0x392B3CCf36C8adB8094F45573346B457414cD752/kpk-usdc-yield">https://app.morpho.org/base/vault/0x392B3CCf36C8adB8094F45573346B457414cD752/kpk-usdc-yield</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-yield-base-vault">https://dune.com/kpk/kpk-usdc-yield-base-vault</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Base</td></tr><tr><td><strong>Protocol</strong></td><td><a href="https://morpho.org/">Morpho</a></td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://basescan.org/address/0x392B3CCf36C8adB8094F45573346B457414cD752#code">0x392B3CCf36C8adB8094F45573346B457414cD752</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥30%</td></tr><tr><td><strong>Fees</strong></td><td>0% on performance</td></tr><tr><td><strong>forceDeallocation penalty</strong></td><td>0.01%</td></tr></tbody></table>

### Strategy

The vault supplies USDC to approved Morpho markets and allocates across them using tier-based rules. Allocations are executed through a [Morpho v2 adapter component](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) that routes deposits directly into individual Morpho V1 markets. Markets are enabled only after passing due diligence under KPK’s [Risk Framework](/vaults/infrastructure/risk-framework), and each is assigned a risk tier and a per-market cap to limit concentration and enforce diversification.

The vault launches with three markets. **savUSD/USDC** (Avant) uses the same collateral as [KPK USDC Yield (Ethereum)](/vaults/vaults/morpho/ethereum/usdc-yield) and was deployed by KPK on Base. **cbBTC/USDC** is the largest market on Base by supplied and available liquidity, and gives the vault its deepest withdrawal capacity. **mGLO/USDC** adds a tokenised private-credit leg through Midas Fasanara Global Open. Allocation across the three markets follows the tier- and cap-aware rules above, weighing yield against withdrawal liquidity.

Vault management is [fully automated](/vaults/infrastructure/automation) through three dedicated agents operated by KPK: a rebalance agent, an exit agent, and a monitoring agent. The agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields, alongside depositor and third-party activity against the vault itself.

### Risk framework

This vault follows KPK’s [Risk Framework](/vaults/infrastructure/risk-framework) for market selection (onchain/offchain review, external signals), tiering, and ongoing monitoring. Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

#### Risk-tier snapshot

<table><thead><tr><th width="168.8359375">Market exposure</th><th width="108.640625">Issuer</th><th width="104.0234375">Risk tier</th><th width="141.45703125" align="right">Allocation cap</th><th width="83" align="right">LLTV</th><th>Oracle</th></tr></thead><tbody><tr><td><a href="https://app.morpho.org/base/variable/0x9103c3b4e834476c9a62ea009ba2c884ee42e94e6e314a26f04d312434191836/cbbtc-usdc">cbBTC/USDC</a></td><td>Coinbase</td><td>A</td><td align="right">90%</td><td align="right">86%</td><td>Chainlink</td></tr><tr><td><a href="https://app.morpho.org/base/variable/0x4217fed63c853537fb818f4ba4416bc708c3d7534913532feb34c3511ce2aba6/savusd-usdc">savUSD/USDC</a></td><td>Avant</td><td>C</td><td align="right">40%</td><td align="right">91.5%</td><td>Fundamental + Chainlink</td></tr><tr><td><a href="https://app.morpho.org/base/variable/0x29ae7ac08be3a58e11151fed74701fb8e6ffd8e76b1870cf17ad70f332752d0b/mglo-usdc">mGLO/USDC</a></td><td>Midas/Fasanara</td><td>D</td><td align="right">40%</td><td align="right">91.5%</td><td>Midas</td></tr></tbody></table>

#### Key risks

* Liquidity and utilisation risk in underlying markets affecting withdrawal latency
* Oracle risk (manipulation, staleness, or failure), affecting pricing and liquidations
* Concentration risk across enabled markets
* Smart-contract and dependency risk (Morpho, collateral assets, and oracle systems)

These risks are actively monitored and managed, but cannot be fully eliminated. See the [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/morpho#governance-and-control) designed for transparency, security, and timely response. The [Curator and allocator Safe](https://app.safe.global/home?safe=base:0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0) (2/4), with a Permissions Layer for agents, is\
`0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0`.


# Change log

This page records **all major configuration and parameter changes** to KPK-curated Morpho vaults.

Entries are listed in reverse chronological order. For current parameters, see the individual pool pages.

<table><thead><tr><th width="120.96875">Date</th><th width="222.890625">Scope</th><th width="260.2578125">Change</th><th width="327.7781982421875">Description/Rationale</th><th width="338.625">Tx/Reference</th></tr></thead><tbody><tr><td>2026-09-03</td><td>USDC Prime</td><td>Hard shutdown of <a href="https://app.morpho.org/ethereum/variable/0x34377fc4f617c51818e92c79df31ff270c6a91bc94ad32e367fdf59b9f4ac5dd/weeth-usdc">weETH/USDC</a> market; sweep stranded remnant</td><td>Low borrow demand; market shut down and remaining funds swept.</td><td><a href="https://etherscan.io/tx/0x3f7a618ac40b57f637f0a69758a2c7a889d5101266e87df17fab036375fea146">shutdown</a></td></tr><tr><td>2026-08-31</td><td>USDC Yield (Base)</td><td>Deployed a Roles Modifier and granted rebalance, monitoring, and shutdown agent roles on the Curator Safe</td><td>Enables 24/7 automation (rebalancing, monitoring, and exit agents) on the newly deployed Base vault.</td><td><ul><li><a href="https://app.safe.global/transactions/tx?safe=base:0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0&#x26;id=multisig_0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0_0xd6c86e4c2bd5a10de24812777f075a2ba1a3075dd4625d24823699abb5a5e6aa">add Roles Modifier</a></li><li><a href="https://app.safe.global/transactions/tx?safe=base:0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0&#x26;id=multisig_0x58C83Ce55164537F9C73d5B65C426691bC26Ffa0_0x45859b94155d979f44a9e1569c73e44558a2cbd7676c9ca0d00296814aaa1fb9">add agent roles</a></li></ul></td></tr><tr><td>2026-08-27</td><td>USDC Yield (Base)</td><td>Deployed <strong>KPK USDC Yield</strong> vault on Morpho v2 on Base; enabled savUSD/USDC, cbBTC/USDC, and mGLO/USDC markets; set 3-day (cap increase, add adapter) and 7-day (remove adapter, increase timelock) timelocks; transferred ownership to the Security Council Safe</td><td>Inaugural KPK-curated Morpho vault on Base.</td><td><a href="https://app.morpho.org/base/vault/0x392B3CCf36C8adB8094F45573346B457414cD752/kpk-usdc-yield">vault</a></td></tr><tr><td>2026-08-24</td><td>USDC Yield (Base)</td><td>Deployed savUSD/USDC market (Avant, 91.5% LLTV, savUSD/avUSD exchange-rate oracle with Chainlink USDC/USD)</td><td>Market deployed for the new Base vault.</td><td><ul><li><a href="https://basescan.org/tx/0x71fa0e760ef555c46dbe4825bf5d77d117b7d97c5d1d5d73b502e19677147153">create market</a></li><li><a href="https://app.morpho.org/base/variable/0x4217fed63c853537fb818f4ba4416bc708c3d7534913532feb34c3511ce2aba6/savusd-usdc#market">market</a></li></ul></td></tr><tr><td>2026-08-17</td><td>USDC Yield RWA</td><td>Granted the rebalancing, monitoring, and shutdown agent EOAs their scoped roles on the Curator Safe's Roles Modifier</td><td>Activates KPK's automated curation stack (rebalance, exit, monitoring) for the vault.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4&#x26;id=multisig_0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4_0xfa0bbce85f05c455005b7669e106905d72819488b1a600ecc6b5f38de8a698d0">Add roles</a></td></tr><tr><td>2026-08-10</td><td>USDC Yield</td><td>Offboard <a href="https://app.morpho.org/ethereum/variable/0xae60b71b407e0517ead445b7113a7ffa07ea4a9379d526ade541a3e9ec777cb4/snusd-usdc#overview">sNUSD/USDC</a> market; re-onboard <a href="https://app.morpho.org/ethereum/variable/0xbbf7ce1b40d32d3e3048f5cf27eeaa6de8cb27b80194690aab191a63381d8c99/siusd-usdc">siUSD/USDC</a> market (Tier C, 60% cap)</td><td>Precautionary shutdown following a decline in sNUSD/USDC market liquidity; siUSD/USDC re-onboarded at its prior risk profile to backfill capacity.</td><td><ul><li>sNUSD/USDC — <a href="https://etherscan.io/tx/0x30159e9ee4dfc24070e44cda857c4b4f5ebd86922334cdca41f3d494f2d7f631">shutdown</a></li><li>siUSD/USDC — <a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x662cbaa6fa588e85ba8e6dc6537b95eb9015e252ae3300c56fdf71de45f54c23">set caps</a></li></ul></td></tr><tr><td>2026-08-06</td><td>USDC Yield RWA</td><td>Deployed and enabled a Roles Modifier on the Curator Safe</td><td>Standard Permissions Layer rollout ahead of agent onboarding.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4&#x26;id=multisig_0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4_0xd09da3a9cf0327caedec35b383df71199cbfe6e8c8923bada85930359eeaeeea">Enable Roles Modifier</a></td></tr><tr><td>2026-08-04</td><td>ETH Yield</td><td>Offboard Origin ARM markets (<a href="https://app.morpho.org/ethereum/variable/0x2049bea9dfae8189895616ff4bf229c9c664e266b29c9ffe692b100be4879714/arm-weth-steth-weth">stETH</a> &#x26; <a href="https://app.morpho.org/ethereum/variable/0x883b22b724b422729ec0688e6ed71da1c5105ac6610810624f25263d7c2e7bf3/arm-weth-eeth-weth">eETH</a>); set allocation caps to 0</td><td>Origin is deprecating the ARM vaults, alongside declining borrow demand on both markets.</td><td><ul><li>ARM-WETH-stETH — <a href="https://etherscan.io/tx/0xa3b5e38d53ec6d2d0e499a8aa59b262cb3b580ca437ff4f46d697e83efc91781">setCaps</a></li><li>ARM-WETH-eETH — <a href="https://etherscan.io/tx/0x342ef27675fa2c7260b59223d8f2f934c1c7ac0314ad44d9a0ad9fecbd9c9f3f">setCaps</a></li></ul></td></tr><tr><td>2026-07-31</td><td>wARS Yield</td><td>Enabled the Roles Modifier on the Curator Safe and granted the rebalancing, monitoring, and shutdown agent EOAs their scoped permissions (including <code>deallocate</code> on the rebalancer role).</td><td>Activates KPK's automated curation stack (rebalance, exit, monitoring) for the vault, following the standard Permissions Layer rollout.</td><td><ul><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xEaCFA855d172F1B40D9b04245960b2c12266C5f5&#x26;id=multisig_0xEaCFA855d172F1B40D9b04245960b2c12266C5f5_0xd9b28abd88306217dc80274b3491ed082815e87fd6328bba3a3edf203e3828b0">Enable Roles Modifier</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xEaCFA855d172F1B40D9b04245960b2c12266C5f5&#x26;id=multisig_0xEaCFA855d172F1B40D9b04245960b2c12266C5f5_0xd7ae13bb79c4236ba60d6800e7d5b8d869d35e553d5a62c5676e2f9bb166e5bd">Add roles (Tx 1)</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xEaCFA855d172F1B40D9b04245960b2c12266C5f5&#x26;id=multisig_0xEaCFA855d172F1B40D9b04245960b2c12266C5f5_0xd3186f46df3057ff8f1d08439d66a3a9c4347574e71c8912a633ec5921376bc0">Add roles (Tx 2, deallocate fix)</a></li></ul></td></tr><tr><td>2026-07-29</td><td>wARS Yield</td><td>Set function-specific timelocks: <strong>3 days</strong> for add adapter, increase absolute cap, and increase relative cap; <strong>7 days</strong> for increase timelock and remove adapter.</td><td>Aligns the vault with KPK's standard timelock schedule.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xEaCFA855d172F1B40D9b04245960b2c12266C5f5&#x26;id=multisig_0xEaCFA855d172F1B40D9b04245960b2c12266C5f5_0x06364ecdb6f29e89d9900d59578272fb8f1dbf2ab3f49783ea0a35c841c22120">Safe batch</a></td></tr><tr><td>2026-07-28</td><td>wARS Yield</td><td>Set <code>maxRate</code> to 31709791983 (100% APR cap); set <code>forceDeallocate</code> penalty to 1e14 (0.01%); raised Curator Safe threshold from 1/5 to 2/5; initiated Owner transfer from the deployer EOA to the Security Council Safe.</td><td>Aligns the vault with KPK's standard configuration.</td><td>—</td></tr><tr><td>2026-07-28</td><td>wARS Yield</td><td>Enabled WBTC/wARS market (Tier C, 62.5% LLTV) and added it to the vault.</td><td>Completes the four planned collateral markets once the WBTC/wARS oracle composition was finalised.</td><td><a href="https://app.morpho.org/ethereum/variable/0x7730ef5352412a37d441ab960f5c8f33ea8d1c9f01c34fc2340f65fa1c59ce69/wbtc-wars#market">WBTC/wARS market</a></td></tr><tr><td>2026-07-28</td><td>wARS Yield</td><td>Enabled WETH/wARS, USDC/wARS, and USDT/wARS markets (Tier C; 62.5% LLTV for WETH, 77% LLTV for USDC and USDT) and added them to the vault.</td><td>First markets live for the vault, deployed ahead of the WBTC/wARS oracle (more complex composition, finalised same day).</td><td><ul><li><a href="https://app.morpho.org/ethereum/variable/0x9666c9b61cc58e3187c6e344697ed31fe558dc01809740417beee07dc77d96b3/weth-wars#market">WETH/wARS market</a></li><li><a href="https://app.morpho.org/ethereum/variable/0x7bb3c45550c02e7e00ddbf2f847717dc4485df4bbae3b992178a74f0d45c4e87/usdc-wars#market">USDC/wARS market</a></li><li><a href="https://app.morpho.org/ethereum/variable/0x8e6271e428ee5b41c98a3765919e359f16b0c6261c85d84f9b9b8b5242ab9c9e/usdt-wars#market">USDT/wARS market</a></li></ul></td></tr><tr><td>2026-07-28</td><td>wARS Yield</td><td>Deployed <strong>KPK wARS Yield</strong> vault on Morpho v2, seeded the dead deposit, and deployed the Curator Safe (initially 1/5) as curator.</td><td>Inaugural KPK-curated wFIAT lending vault, using wARS (Ripio's Wrapped Argentine Peso) as the loan asset against WBTC, WETH, USDC and USDT collateral. Rerun of the Monerium EURe vault playbook.</td><td><a href="https://etherscan.io/address/0xb5ce3CA2C774b72955C25875022FdD91f7a7B938">Vault contract</a></td></tr><tr><td>2026-07-28</td><td>USDC Yield RWA</td><td>Transferred ownership to the Security Council Safe; granted the Allocator role to KPK Deployer 1 and removed the deployment-time allocator</td><td>Aligns the vault with KPK's standard role configuration.</td><td><ul><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4&#x26;id=multisig_0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4_0x16cbb4d4cf24955b050336915f21a9b45e8a10a6ea73fef44592d78c81a97e98">setOwner</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4&#x26;id=multisig_0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4_0xabb4b703a03069bd64cef2860ea5182eef3d750f9f9ca05b7998820f651106d1">remove allocator</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4&#x26;id=multisig_0xFfbCF26270A90FCdAAF56AcF6e235730B04546c4_0xb5db4451b3edbef2cce17491dad4939bf233d29195dbad65248b68e5c53c9be8">add allocator</a></li></ul></td></tr><tr><td>2026-07-27</td><td>USDC Yield RWA</td><td>Deployed <strong>KPK USDC Yield RWA</strong> vault on Morpho v2 with a Markets V1 Adapter and the Curator Safe (2/5) as curator; enabled wFalconX/USDC, AA_FalconXUSDC/USDC, USD3/USDC, PT-USD3-17DEC2026/USDC, and mF-ONE/USDC markets; set the 3-day cap and adapter, 7-day removal, and 14-day fee timelocks, the 100% APR maxRate, and the 0.01% forceDeallocate penalty</td><td>Inaugural KPK vault lending against tokenised real-world-asset collateral, supplying only to markets shared with other curators.</td><td><a href="https://etherscan.io/address/0x7a72bcD2c3F7F7e4D6679170a0625bAB15D7DDa1#code">Vault contract</a></td></tr><tr><td>2026-07-17</td><td>All V2 vaults (USDC Prime, USDC Prime Core, USDC Yield, USDT Prime, ETH Prime, ETH Yield, USDC Yield Arb, EURC Yield)</td><td>Set <code>forceDeallocate</code> penalty to 0.01% (from 0%)</td><td>Raised the default penalty on every V2 vault in response to a <code>forceDeallocate</code> griefing pattern.</td><td><ul><li>USDC Prime — <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xcb40d529540c2875e06962864902e069b128df70417b83fd809be3349552aace">setForceDeallocatePenalty</a></li><li>USDC Prime Core — <a href="https://app.safe.global/transactions/tx?safe=eth:0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E&#x26;id=multisig_0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E_0xe6fbd4436ded4653a49c63b303a74e37c76f7531b458e9762d81dfa8f8539f85">setForceDeallocatePenalty</a></li><li>USDC Yield — <a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xefbd1284428ae9827c8cec00f29ff5958ff15b2d8c0446ae140041e686128a4c">setForceDeallocatePenalty</a></li><li>USDT Prime — <a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x6055261d535132e9cfed22106f3d1b2930d7ac5dd4fd45089833de7c2bd0cc6b">setForceDeallocatePenalty</a></li><li>ETH Prime — <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xe7d6939e62e83deaaebb0d7ea213861b3f5cfa046b0f4bcd7b2dcbfc6f6f84a4">setForceDeallocatePenalty</a></li><li>ETH Yield — <a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0xe79e3837ff2a89d7ea8397128c10b7314f1b2992e8b2e54b8410f8136f1306a4">setForceDeallocatePenalty</a></li><li>USDC Yield Arb — <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x94dd60383485187fbb08d678672c2ba467e09daa59766f01388cb57fdbddb6b0">setForceDeallocatePenalty</a></li><li>EURC Yield — <a href="https://app.safe.global/transactions/tx?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5&#x26;id=multisig_0xd15f11B334e1e233127302E5F759C17DA1260df5_0x8ccd9f47e85346acdfd22214fb7641731448408c0c8262c30f4ce3cf27d3f127">setForceDeallocatePenalty</a></li></ul></td></tr><tr><td>2026-07-16</td><td>USDC Yield</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0xeb17955ea422baeddbfb0b8d8c9086c5be7a9cfdefb292119a102e981a30062e/stcusd-usdc">stcUSD/USDC</a> market; set caps to 0 and deallocate</td><td>Market liquidity dwindled; vault exposure had already been reduced to dust as a precaution.</td><td><a href="https://etherscan.io/tx/0xfd31cc2ff369c78e4d7dfc16a2a2adcd1b76386afbae212dea83ec31124e09dd">setCaps + deallocate</a></td></tr><tr><td>2026-07-08</td><td>ETH Prime</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0x138eec0e4a1937eb92ebc70043ed539661dd7ed5a89fb92a720b341650288a40/wbtc-weth">WBTC/WETH</a> market; set allocation cap to 0</td><td>Borrow demand shrunk below what we consider a useable level.</td><td><a href="https://etherscan.io/tx/0x7c3f84d4cc89e0cfb3dc7ee5e6995af274383385f6dbe95e16de2ddeb81a75dc">setCaps</a></td></tr><tr><td>2026-07-08</td><td>ETH Yield</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0x5f8a138ba332398a9116910f4d5e5dcd9b207024c5290ce5bc87bc2dbd8e4a86/eth-weth">ETH+/WETH</a> market; set allocation cap to 0</td><td>Borrow demand shrunk below what we consider a useable level.</td><td><a href="https://etherscan.io/tx/0x848b3fec9f27b1184ffa25ae3064483296cba3d6595320c83fa8aaa2f695547d">setCaps</a></td></tr><tr><td>2026-07-03</td><td>USDC Yield</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0x88ab06d43b089e574bbe312deee6cdb292702c3a2e765059492c34b1cd298acf/ftusd-usdc#risk">ftUSD/USDC</a> market</td><td>Add Flying Tulip ftUSD as a new collateral market.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x176baba9085890e889432ee1316d31cdf8455ae86f0859c800eff04f8ac2001d">setCaps</a></td></tr><tr><td>2026-07-02</td><td>USDC Yield</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0xfa87949abc5866c303f172dccd59fbd57591dee9233b4cd90035906a689eff48/dusd-usdc">DUSD/USDC</a> market; set allocation cap to 0</td><td>Limited borrow demand in the market did not justify continued allocation.</td><td><a href="https://etherscan.io/tx/0x131db224febb7b25ce50f1fde8074cd82bed2ba91eddf6f1cce65b92eb8aa597">setCaps</a></td></tr><tr><td>2026-06-23</td><td>USDC Prime, USDC Prime Core, USDC Yield, EURC Yield and ETH Yield</td><td>Reset <code>forceDeallocatePenalty</code> to 0% (0 bps)</td><td>Restored force deallocate fee to 0%.</td><td><ul><li>USDC Yield — <a href="https://etherscan.io/tx/0xe807d8d26cc784da72634339dba688c186538c3857402f883186909f5a9884fa">setForceDeallocatePenalty</a></li><li>USDC Prime — <a href="https://etherscan.io/tx/0x8f394bc9a1c876217edee9719db2d36635b422ee307eda7caa38093bbfb8376f">setForceDeallocatePenalty</a></li><li>ETH Yield — <a href="https://etherscan.io/tx/0x24b6abc43292d0c7d9c97e79193ff4e513762b65034c05568c39bad8ed259c88">setForceDeallocatePenalty</a></li><li>USDC Prime Core — <a href="https://etherscan.io/tx/0x9560d0b539244f2470e9aa9d99a6433eff9e1a0aa379d85074455e914c5705db">setForceDeallocatePenalty</a></li><li>EURC Yield — <a href="https://etherscan.io/tx/0x61242d71ed4fa4dec5332121c57754f9d3afb748d7d2e2aaf4193aa428aa795a">setForceDeallocatePenalty</a></li></ul></td></tr><tr><td>2026-06-22</td><td>USDC Yield</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0x34377fc4f617c51818e92c79df31ff270c6a91bc94ad32e367fdf59b9f4ac5dd/weeth-usdc">weETH/USDC</a> market</td><td>Add EtherFi weETH as an overflow collateral market to absorb large incoming deposits. Queued through 3-day timelock.</td><td><ul><li><a href="https://etherscan.io/tx/0xbb693d29b9ea956f00cc0078ec96ff01a4d29446350bd87f4508625428011bc4">weETH collateral cap</a></li><li><a href="https://etherscan.io/tx/0x809215894a3702ed5d5346ca4af6dfcbe0cd73c46808023f6d89925c7ab1a987">weETH/USDC market cap</a></li></ul></td></tr><tr><td>2026-06-21</td><td>USDC Prime, USDC Prime Core, USDC Yield, EURC Yield and ETH Yield</td><td>Set <code>forceDeallocatePenalty</code> to 0.5% (50 bps)</td><td>Raised deallocate fee precautionarily following a griefing attack</td><td><ul><li>USDC Prime — <a href="https://etherscan.io/tx/0xe50127dd5ed5d8705919655bb930e75c4310c4a145bfd0577a629717ddda9f7c">setForceDeallocatePenalty</a></li><li>USDC Prime Core — <a href="https://etherscan.io/tx/0x1b6f34c8d606ee03dff68c0162d7a2d2a7824109bb3b91ad3c813da4a9165a0b">setForceDeallocatePenalty</a></li><li>USDC Yield — <a href="https://etherscan.io/tx/0x6601b8ffa2a8b6b302f45f3f30e999fcdc3920579e041e4990c0d1b46579ef4b">setForceDeallocatePenalty</a></li><li>EURC Yield — <a href="https://etherscan.io/tx/0xa915df1595778bf783af60eb2cc2590242b629258b6540a775fcdb29f5864db8">setForceDeallocatePenalty</a></li><li>ETH Yield — <a href="https://etherscan.io/tx/0x1ef4d3682cc8702d5f7035eb43663cd6111387b4ca1497351aa825907b1e3ba8">setForceDeallocatePenalty</a></li></ul></td></tr><tr><td>2026-06-04</td><td>USDC Yield</td><td>Offboard <a href="https://app.morpho.org/ethereum/variable/0xb62aac664f81d19f21a158aa0373967ef60fd1ac8de4a9091bd225c007973ca6/pt-snusd-4jun2026-usdc">PT-sNUSD-4JUN2026</a></td><td>PT-sNUSD-4JUN2026 reached maturity today; permanently offboarded post-expiry.</td><td><a href="https://etherscan.io/tx/0x41a6c1a6104644587bbe39f61f6e2df6ce27b407704258bb64b3d105d8f1c83b">shutdown</a></td></tr><tr><td>2026-06-04</td><td>USDC Yield</td><td>Offboard <a href="https://app.morpho.org/ethereum/variable/0xbbf7ce1b40d32d3e3048f5cf27eeaa6de8cb27b80194690aab191a63381d8c99/siusd-usdc">siUSD/USDC</a> market</td><td>Precautionary shutdown following a decline in siUSD/USDC market liquidity.</td><td><a href="https://etherscan.io/tx/0x34c05c01c94987f90c87ae7adf4fd571f33d65e1c139b874b9abad3c4228493f">shutdown</a></td></tr><tr><td>2026-05-28</td><td>USDC Prime</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0x34377fc4f617c51818e92c79df31ff270c6a91bc94ad32e367fdf59b9f4ac5dd/weeth-usdc">weETH/USDC</a> market</td><td>New market with healthy liquidity and borrow demand. Adds EtherFi weETH as a Tier B collateral yield source to USDC Prime. Queued through 3-day timelock.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x6113f73ca41b5d1cf431cf03c483207a712b4a34cc3a26fbe337dea22746bd82">submitCap</a></td></tr><tr><td>2026-05-28</td><td>ETH Prime, USDC Yield, USDC Yield (Arb)</td><td>Offboard underused markets: <a href="https://app.morpho.org/ethereum/variable/0x5f8a138ba332398a9116910f4d5e5dcd9b207024c5290ce5bc87bc2dbd8e4a86/eth-weth">ETH+/WETH</a> (ETH Prime); <a href="https://app.morpho.org/ethereum/variable/0x54cebd0c5d5ad84f551a883991c39c470e081a20452eaef47eec2377ffae9f98/jrusde-usdc">jrUSDe</a>, <a href="https://app.morpho.org/ethereum/variable/0x5cebfae10f5e88d33df2421923f3d9f32359429fda2f78edacc9b4fdb09b0553/pt-usdg-28may2026-usdc">PT-USDG-28MAY2026</a>, <a href="https://app.morpho.org/ethereum/variable/0xc2bc5e1e304fb1ea103dcbee37ece3c7e9219fb4b2b19d8ffdf81c39f4fbf180/pt-srnusd-28may2026-usdc">PT-srNUSD-28MAY2026</a> (USDC Yield); <a href="https://app.morpho.org/arbitrum/variable/0x77fe2f7c2dd6f4da6bc5f445b06052ff8df55cb70cfce9afc16ec3c69a5fd3a3/susds-usdc">sUSDS/USDC</a>, <a href="https://app.morpho.org/arbitrum/variable/0xf86f3edd6f16cd8211f4d206866dc4ecd41be6211063ac11f8508e1b7112ef40/syrupusdc-usdc">syrupUSDC/USDC</a> (USDC Yield Arb)</td><td>Continuing derisking of underused markets. ETH+/WETH: low borrow demand. jrUSDe: low liquidity. PT-USDG-28MAY2026 and PT-srNUSD-28MAY2026: matured today, offboarded post-expiry. sUSDS/USDC and syrupUSDC/USDC: low borrow demand on Arbitrum.</td><td><ul><li>ETH+ — <a href="https://etherscan.io/tx/0x8d0150f653717dbf6b903175d0db68e34c759a14bd1a4b7fceca35208a78b628">shutdown</a></li><li>jrUSDe — <a href="https://etherscan.io/tx/0x9bcc4216712ec8d094a5d12f6226f3d6fe41eeb27580740e5d3245f483999e1f#eventlog">shutdown</a></li><li>PT-USDG — <a href="https://etherscan.io/tx/0x7a45a2c3669cd5f3bb6ddb5ff0e8f81d6d261cb31da68a3b177596df80d90b2c">shutdown</a></li><li>sUSDS — <a href="https://arbiscan.io/tx/0xdc76b86d4a5baf94cfc0fa33df8675ae977f56fcda0803c328195907b2b8fe99">shutdown</a></li><li>syrupUSDC — <a href="https://arbiscan.io/tx/0x45810f096775595416cc4c37578ae3dd7b1d795651ef176172e18326fea8d070">shutdown</a></li><li>PT-srNUSD — <a href="https://etherscan.io/tx/0xa738ceb9e466a3078b7b7b1fbdbb11e0793f17aa19135bd4e0e3644f5c74a6a2">shutdown</a></li></ul></td></tr><tr><td>2026-05-22</td><td>USDC Yield</td><td>Offboard PT-savUSD-14MAY2026/USDC</td><td>PT-savUSD expired, permanently offboarded. With PT-savUSD gone, expanding spot savUSD cap to capture Avant's underlying USD yield (allocation cap 60% → 85%)</td><td><a href="https://etherscan.io/tx/0x070aa36f45ebb42bc57f8fcaa43b68ec9b7a9c0698736682b1ebf61504bfc6b7#eventlog">decreaseCaps</a></td></tr><tr><td>2026-05-14</td><td>ETH Prime, USDC Yield</td><td>Onboard new markets: <a href="https://app.morpho.org/ethereum/variable/0x251b7cbc2c33ba4eabe3b7163ad0fdd3cfb97de80d8377f0f48b8d31aee7606f/reth-weth">rETH/WETH</a> (ETH Prime) and <a href="https://app.morpho.org/ethereum/variable/0xfa87949abc5866c303f172dccd59fbd57591dee9233b4cd90035906a689eff48/dusd-usdc">DUSD/USDC</a> (USDC Yield)</td><td>Cover borrow demand from existing counterparties. Queued through 3-day timelock.</td><td><ul><li>ETH Prime — <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x89d0774402522d5425801411fc8976c9e94b9e35b537ac7a514e22d63e203a2b">setCaps</a></li><li>USDC Yield — <a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x702423e0c10ff8a7c6d6adbd1743abdb525a86572538380a8f12a63bc1cf0995">setCaps</a></li></ul></td></tr><tr><td>2026-05-13</td><td>All V2 vaults</td><td>Onboard new operator <code>0xAF150C6D108D0C2c96BbFc3CeF9B4848B5d99440</code> as Safe owner and allocator; grant <code>MORPHO_SHUTDOWN_AGENT</code> permission to call <code>setForceDeallocatePenalty</code></td><td>Add new operator across all curator Safes (thresholds unchanged) with allocator role on each V2 vault; expand the shutdown agent scope to raise the deallocation penalty in response to griefing.</td><td><ul><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E&#x26;id=multisig_0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E_0x5ebc84ebb788b1dc27ab9bc71cfde2fead8dc0c8a77e314f3dd7a9ec2a9cf4f3">USDC Prime Core</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x2c884177fcfc0cfb1198b75910b5d050cf2164a9af0268847be2b57d2ac3e497">USDC Prime</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xa49747bb306a5666db73237086450ced0177321eeb99c6b48548a332eb8c42ce">USDC Yield</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xa3232c4982883ce6adb0278fb766380766002f45ce82e009308d67753d8125c0">ETH Prime</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0x59f91b4ac2750f49ec7b5352645a84f60fce0dae3fc612a06cd6e3f89ddc74e7">ETH Yield</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5&#x26;id=multisig_0xd15f11B334e1e233127302E5F759C17DA1260df5_0xa7630d5bd37a56b21ba38cf13525c1c402b9bb2c9634ac778a13f8bbdacac802">EURC Yield</a></li><li><a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x7d6dbae564fb5da328ede583c58b918b70368614d824004306ba1451deb4e80f">USDT Prime</a></li><li><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x023a82887e8d05f2a6171f8d1aaa58e21532982444d9d8b01956a61950d5b4c1">USDC Yield Arb</a></li></ul></td></tr><tr><td>2026-05-12</td><td>All V2 vaults (USDC Prime, ETH Prime, USDC Yield, EURC Yield, ETH Yield, USDT Prime, USDC Yield Arb)</td><td>Rebrand vaults: prepend <code>KPK</code> to <code>name</code> and <code>symbol</code></td><td>Align on-chain vault identities with KPK brand. <code>name</code> → <code>KPK &#x3C;Vault></code>, <code>symbol</code> → <code>KPK_&#x3C;Vault></code>. Executed by Security Council.</td><td><ul><li>Ethereum (5 vaults bundled) — <a href="https://app.safe.global/transactions/tx?safe=eth:0x354C92aF243d53A24feb3dFF20372Af7b7c47478&#x26;id=multisig_0x354C92aF243d53A24feb3dFF20372Af7b7c47478_0x2773d8acecfa0c9c894463050368cd9662155d7774b7d07ac94d8dc2c8814f0a">setName/setSymbol</a></li><li>Arbitrum (USDC Yield Arb) — <a href="https://app.safe.global/transactions/tx?safe=arb1:0x354C92aF243d53A24feb3dFF20372Af7b7c47478&#x26;id=multisig_0x354C92aF243d53A24feb3dFF20372Af7b7c47478_0xc5cd68f1d23a4588f4b12ae0e310ae0889ac16ff865b1edfea3bc8035816fbe1">setName/setSymbol</a></li></ul></td></tr><tr><td>2026-05-11</td><td>USDC Prime</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0x94b823e6bd8ea533b4e33fbc307faea0b307301bc48763acc4d4aa4def7636cd/weth-usdc">WETH/USDC</a> market</td><td>Add WETH as collateral option — additional yield source with deep liquidity.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x4ea58e301e78da4c59de296b17fa2a6ae2fe9e11478406c0ded68dc5a90b7cd6">submitCap</a></td></tr><tr><td>2026-05-08</td><td>USDC Prime, USDC Yield, USDC Yield (Arb)</td><td>Offboard underused markets; set allocation caps to 0</td><td>Remove markets below 1% of vault interest over 90 days. USDC Prime: weETH, tBTC, LBTC. USDC Yield: PT-cUSD-23JUL2026, MORPHO, YFI, srRoyUSDC. USDC Yield (Arb): PT-thBILL-18JUN2026.</td><td><ul><li>USDC Prime — <a href="https://etherscan.io/tx/0xb21cb13817376fc7845c8190842b4aa8f27944815a22158915dbcb74fe1015e3">weETH</a>, <a href="https://etherscan.io/tx/0x2537b6bc8ac09d814eaac4e25b6a3356d16631e6b5b61631fe1c2a2d58da7310">tBTC</a>, <a href="https://etherscan.io/tx/0x036e6e55a57c5900bcc1add25d538edbcc5e3055b69b5dde85c031319ba379f2">LBTC</a></li><li>USDC Yield — <a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xaf8ce95d9f572e8d7edf288bb6d44cc44744b8c280f08aba524bb71f3965f389">PT-cUSD, MORPHO, YFI, srNUSD</a></li><li>USDC Yield (Arb) — <a href="https://arbiscan.io/tx/0x5084905ab70e08a6d91b1a96ff5274b7c7b72bb7a2ff04ef3466313e3c5df4aa">PT-thBILL</a></li></ul></td></tr><tr><td>2026-05-06</td><td>USDC Yield V2, ETH Yield V2</td><td>Raise absolute and relative caps on the Markets V1 Adapter route for selected V1 markets. USDC Yield V2: savUSD/USDC and PT-savUSD-14MAY2026/USDC (both 91.5% LLTV). ETH Yield V2: savETH/WETH (91.5% LLTV).</td><td>Raise V1 Adapter caps to allow V2 vaults to absorb V1 vault allocations during migration.</td><td><a href="https://etherscan.io/tx/0x381dc1e6df6294e3b8f51f774acf1e2ddb41a3ff9626a3edfa81b2ecec727b5e">USDC Yield V2</a>, <a href="https://etherscan.io/tx/0xbc8013570baddbd3d3c161610af54b543c8fa09dd36f0b0989743d1a50bbd7d2">ETH Yield V2</a></td></tr><tr><td>2026-05-04</td><td>USDC Prime, ETH Prime, USDC Yield, EURC Yield, ETH Yield, USDC Yield (Arb)</td><td>Switch curated allocation path on V2 vaults from the Vault V1 Adapter to the Markets V1 Adapter; remove the legacy Vault V1 Adapter from each V2 vault</td><td>V2 vaults now allocate directly into V1 markets via the Markets V1 Adapter; legacy MetaMorpho vault layer removed from the curated path.</td><td><ul><li><a href="https://etherscan.io/tx/0x71693b4f2ba820f7741dc236771d217087742e2d22a0da379cd5b7d24682e766">USDC Prime</a></li><li><a href="https://etherscan.io/tx/0xcc7cbb515c5d1245157885e82713178a760c76fc5fe42b04458b499ce0c9a8e8">ETH Prime</a></li><li><a href="https://etherscan.io/tx/0xf02c4cc18a841fe166a29137b4d48ab4be2394f7215c5738697201c3b9caa4fe">USDC Yield</a></li><li><a href="https://etherscan.io/tx/0x0b5626f6b602d8d72bd5922acbfc1108f45ba56c3b9f16bc7ffd1d5986c66e46">EURC Yield</a></li><li><a href="https://etherscan.io/tx/0x9ea6ebc8576f367f625c7922b345942c0353873bb5b7e590ec3ff4b7b5051d95">ETH Yield</a></li><li><a href="https://arbiscan.io/tx/0x8430109769402c24d0db0f0d7a5dcccf9f37e14ae731eedadc56f95918528ff9">USDC Yield (Arb)</a></li></ul></td></tr><tr><td>2026-04-28</td><td>USDC Prime Core (new vault)</td><td>Launch new <a href="https://app.morpho.org/ethereum/vault/0x1a1985F50352b58090eb36425AfdFacbaC7806F4">USDC Prime Core</a> vault on Morpho V2 and complete its operational setup.</td><td>New USDC vault with tighter risk profile than Prime. Setup: curator Safe, Roles module, allocator role, name/symbol.</td><td>14 setup transactions from the <a href="https://app.safe.global/transactions/history?safe=eth:0x7a2CE012Fe37dB488C24e31f5e38816cAd82b45E">USDC Prime Core curator Safe</a> between 2026-04-24 and 2026-04-28.</td></tr><tr><td>2026-04-28</td><td>All V2 vaults (7 curator Safes)</td><td>Grant <code>MORPHO_REBALANCER_AGENT</code> and <code>MORPHO_SHUTDOWN_AGENT</code> roles <code>allocate</code> and <code>deallocate</code> permissions. Additionally grant <code>MORPHO_SHUTDOWN_AGENT</code> the cap-lowering pair <code>decreaseAbsoluteCap</code> and <code>decreaseRelativeCap</code>.</td><td>Activate V2 allocation bots. Rebalancer gets routing only; shutdown agent additionally gets unilateral cap-lowering to drain at-risk markets.</td><td>One <code>allowFunction × 6</code> multiSend per curator Safe (USDC Prime, USDC Yield, USDT Prime, ETH Prime, ETH Yield, EURC Yield, USDC Yield Arb).</td></tr><tr><td>2026-04-22</td><td>USDC Prime, USDC Yield, USDC Yield (Arb), USDT Prime</td><td>Re-enabled markets un-affected from rsETH / Aave contagion. Same day, offboarded the smaller <a href="https://app.morpho.org/ethereum/variable/0x6d2fba32b8649d92432d036c16aa80779034b7469b63abc259b17678857f31c2/wsteth-usdc">wstETH/USDC market</a> on USDC Prime V1 and the smaller cbBTC and wstETH markets on USDC Prime V2 (caps to 0). The primary wstETH/USDC market in USDC Prime stays onboarded.</td><td>Lift precautionary shutdowns for markets unaffected by Aave/rsETH contagion. Offboard smaller duplicate wstETH and cbBTC markets in favour of deeper equivalents.</td><td><ul><li><p>USDC Prime</p><ul><li>tBTC <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x696635890710d812671541d15caf34f2bce253c4d6717a9a541609eb7f57ddf6">submitCap</a></li><li>LBTC <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xc41c7449605612335e4c048acfc32929228865861e31648384aee17d5e6e157f">submitCap</a></li><li>smaller wstETH (V1) <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x8d141597fd7bd781e87e1f5bec71293db9be719ec655bec0bb021c5383fc2bdc">submitCap=0</a></li><li>smaller cbBTC + wstETH (V2) <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xdbf749dbb2743a186b9c91835d4ab660e29e4f095441e13dbf824a135a012a55">cap=0</a></li></ul></li><li><p>USDC Yield (Arb)</p><ul><li>WBTC <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x78e9f5c5766e38b2946d334d6eb5f2664aea1fe8313324256807d7832cd55177">submitCap</a></li><li>weETH <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0xb31ce9d186664520ab4f5c38898c7250a7c18633f8ca7619a34a5504547d40f8">submitCap</a></li></ul></li><li><p>USDC Yield</p><ul><li>PT-savUSD <a href="https://etherscan.io/tx/0xf058d3cb1f9318639aae3e8a2282c066f2a91b0b9b77e97853fdf3f4f9055061">acceptCap</a></li><li>savUSD acceptCap (pending)</li><li>siUSD <a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x864c641c9eb512ea440e0b0ef3fa320c8ef68f2c2a552a336e7cea07086ea8e0">submitCap</a></li></ul></li><li><p>USDT Prime</p><ul><li>syrupUSDT <a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x7f2fd543b688c06b324efb56b646ced22f187d9e1cda72600ac5f5cb52134a62">submitCap</a></li></ul></li></ul></td></tr><tr><td>2026-04-20</td><td>ETH Prime</td><td>Exit rsETH and savETH exposure, re-enable deposits</td><td>Fully offboard rsETH following Kelp DAO exploit, remove savETH to eliminate indirect Aave exposure, and re-enable deposits on the vault.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xc266b1181a80e84edc2c6596718e88e8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xe38dd0f82cb28906dd42364f5c276ba473940aebfeb298000c270b57ac54c1f9">setSupplyQueue</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xc266b1181a80e84edc2c6596718e88e8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x2bc12786ce923cda7541f8395e1700ed6e2c409c7b4cb5b6ae1cc97a3fb804e6">updateWithdrawalQueue</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xc266b1181a80e84edc2c6596718e88e8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xe022662260b4ae18f915429d8bb44aac41acfa95c43d80066a5c008a81b5d969">rsETH dust removal</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xc266b1181a80e84edc2c6596718e88e8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x5ca0845a748a83e77968c8cf42d1b85dc15ce3bd9917fb05e52638121031da1a">savETH dust removal</a>,</td></tr><tr><td>2026-04-18</td><td>USDC Prime</td><td>Precautionary response to Kelp DAO exploit.</td><td>Paused USDC Prime deposits as a precaution (re-enabled same day). ETH Vaults paused.<br>Triggered tETH exit and re-queued rsETH exit.</td><td><a href="https://etherscan.io/tx/0x7f7039ec6782de33963aeb339a34a46fa4bfdba3276fdc8c5af7e6c62ddb0b5f">syrupUSDC=0</a>, <a href="https://etherscan.io/tx/0x9356da6cf8ada8180c8875e03578e3e214239d7e0e5a6d817c13b78e1a0177de">USDC Prime pause</a>, <a href="https://etherscan.io/tx/0x96010cca3fb20812bb87fe9a921a1b5fd8f799ea469e6684ad35d20c07b7343d">ETH Yield pause</a>, <a href="https://etherscan.io/tx/0x23de2c251005507441951938d8cbf6f2ee8b1d6e1d1d82f34d69cccf9f2cca97">ETH Prime pause</a>, <a href="https://etherscan.io/tx/0xb8cfde37db8830b4837bf84326e2a91c14ddc7e638edd6ab348b98bb4acc549e">USDC Prime resume</a></td></tr><tr><td>2026-04-13</td><td>ETH HY</td><td>Removed lsETH market</td><td>Market liquidity didn't improve and growth opportunity uncertain.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=module_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_i38ef9238a6415168c8d9d601480910c14f3b80ec9069e0a4b7e7bcb549419d5f0,1,0">submitCap</a></td></tr><tr><td>2026-04-13</td><td>All V2 vaults</td><td>Deploy a Market V1 adapter to all V2 vaults</td><td>Prepare V2 migration: adapters enable direct V1 market supply through V2 vaults.</td><td><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xd9d10621eaa60cb60444f8bd4525c4a4d77e3fb6ae9c943c7ef696c757198847">USDC Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xf180185d06a270814747bd50b6614774cef223b0c142c843a79ae7852f2e2873">USDC HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xc672648826f64973e70e9c68fb660341b523d8cfc89ecc41f918884a14e93795">ETH Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0xf01536f85288050b4d0ea2a28917540bfb532f852852b7a87156a086034a7200">ETH HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5&#x26;id=multisig_0xd15f11B334e1e233127302E5F759C17DA1260df5_0x2b8c20310bc48db56b1771e7fc8cf2ee59432a16baad4030e3ab19cf8e408328">EURC HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x5502c6db3734ca2e8a66a6fa9f289ae72a324fda98928bf68ef675f9f7aefc29">USDT Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x7c9f9338b357d2a800d15fa1c5132eb9ebb094fb8ec4d3c2160a534cb41bf23f">USDC HY (Arb)</a></p></td></tr><tr><td>2026-04-13</td><td>All V2 vaults</td><td>Set forceDeallocate timelock to 0 days</td><td>Preparation for 0% deallocation penalty and V1 Markets Adapter migration. Replicates V1 withdrawal queue behavior.</td><td><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xd489a33096e8c72df4eafd1e2d8420c2b595fb9111dc16d546d8c8755f8107d3">USDC Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x543585da2e45b9416aa999024a89466cf8084f89690852a0e22269a34344c81c">USDC HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x26c5a45ce5ec9f56db9fcac38a94db8812eb97eee612e0dff03f6ea55846dc24">ETH Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0x659f3f6a975f2cb1503aa9dd1f606031333d1f02a1822c8e7a9322a0c69aeb06">ETH HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5&#x26;id=multisig_0xd15f11B334e1e233127302E5F759C17DA1260df5_0xf1a1137cc3d28ca5f9460a6df9819c8b89d75662ab58a87e7d7fbf7a9779c5ca">EURC HY</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x89fd582f727e8dffece50268bb62ff31b057565792effa53f96e22f1e5b27c53">USDT Prime</a>,</p><p><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0xa867ccbd829f3b9a750a70801d43007593fb96280e87b2a557c733c4e6ce9060">USDC HY (Arb)</a></p></td></tr><tr><td>2026-04-10</td><td>ETH Yield</td><td>Added tETH market</td><td>KPK-deployed tETH market — additional yield source.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0xef0498f3f1ce8489f5658cdb43dd8a62d2a7ba7d25d1b778d105635a8b627bb6">submitCap</a></td></tr><tr><td>2026-04-07</td><td>USDC Prime</td><td>Removal of <a href="https://app.morpho.org/ethereum/variable/0x704e020b95cbf452e7a30545d5f72a241c4238eebf9d1c67657fdd4a488581e0/wbtc-usdc">WBTC</a> market</td><td>liquidity migrated, market unused</td><td><a href="https://etherscan.io/tx/0xa03b4e7da8262f440a3261a5f44ed88041f7ccc5d0a7647177df715f58416a7d">shutdown</a></td></tr><tr><td>2026-03-30</td><td>USDC Prime</td><td>Added newly deployed rETH market (86% LTV)</td><td>KPK deployed <a href="https://app.morpho.org/ethereum/variable/0x0a15460ad263c2186fe0b5df20a8cf71d55f3cfa06de15edcf6138f6b8edd8bf/reth-usdc#overview">rETH/USDC</a> market</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x00ee6604a81e0eb12b1e58fdc27cbc67a09e6fa7ccf342e39f179c872bc04bb0">submitCap</a></td></tr><tr><td>2026-03-30</td><td>USDC Yield</td><td>Roll over Pendle PTs: add PT-USDG-28MAY2026, remove expired PT-srUSDe and PT-jrUSDe</td><td>Remove expired PTs (PT-srUSDe, PT-jrUSDe); add PT-USDG-28MAY2026.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x6b7574932b612e0b59582b657f7d57ce4446365edb5a030c894c666152b9d4dc">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=module_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_i8aaa5ee4d65949bd6b0fcb98ff5d1b1e496cd70e854e7a9a5686c8585382d4f70,1,0">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=module_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_ia7b0821d66cbe3a017188e424c5a0d2e8a149c899de32bc5f6b98d9d595d59a50,1,0">submitCap</a></td></tr><tr><td>2026-03-26</td><td>USDC Yield</td><td>Removed deprecated Pendle PT</td><td>PT-siUSD-26MAR2026 removal</td><td><a href="https://etherscan.io/tx/0x6825bc008e1257f078ea541f4c9f1571bfdd9bcdbc4006e252fd381fdc46597f#eventlog">setCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xbbfa9584221f6dec3fdf0b258176400d627bbcd408c0e9f71dc83dc8a2b7fdeb">updateWithdrawQueue</a></td></tr><tr><td>2026-03-23</td><td>USDC Yield</td><td>Reallocate RLP dust, update withdraw queue and re-enable supply.</td><td>Supply queue management ahead of RLP market withdrawal.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x93628d1e19d52d7e7efdd13ee82e88ae7b453980e1c8860ae258994a571e2ed5">reallocate dust,</a><br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x6d405da3c662393506592edce6ae07979ea8d7cd5a1a026fa02ac4ff4c60cbd1">updateWithdrawaQueue</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x3826e0914e16325200a719d0f537c0e777cfe0c0ae10dabebed1eadaf95ecefd">setSupplyQueue</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xd0913ea3874e9c3a6aec0b96271eabdab146a1c062c8f6b6b41ad722a5ca0b7c">submitCap</a></td></tr><tr><td>2026-03-22</td><td>USDC Yield (Arbitrum)</td><td>Remove <a href="https://arbiscan.io/tx/0xd4edfff189bad4b54ccec2af7eacdc5b725b91dabe3e071bbba44535a6320de1">syrupUSDC market</a></td><td>Avoid potential second-order exposure to Resolv</td><td><a href="https://arbiscan.io/tx/0xd4edfff189bad4b54ccec2af7eacdc5b725b91dabe3e071bbba44535a6320de1">shutdown</a></td></tr><tr><td>2026-03-22</td><td>USDC Yield (Ethereum &#x26; Arbitrum)</td><td>set allocation cap to zero on RLP markets following USR exploit</td><td>RLP/USDC capped at zero</td><td><a href="https://etherscan.io/tx/0x6422ac5ea6bc3aefdfaa59f6fbe3379024d6fc8f411190576a48e27c2323e9d0#eventlog">setCap</a> (eth),<br><a href="https://arbiscan.io/tx/0xc1c766907a4db71e2eec20934ee160b315e24b3c984e26f3d377d279f0d03158#eventlog">setCap</a> (arb)</td></tr><tr><td>2026-03-19</td><td>USDC Yield &#x26; Prime</td><td>Onboard new PT, migrate syrupUSDC from USDC Yield to Prime</td><td>Add PT-sNUSD-4JUN2026 to USDC Yield; migrate syrupUSDC from USDC Yield to USDC Prime.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xcf1032d683018419d17d261fc32486dbfe41e1986cd79e1b56c0c73d8177440b">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0xeb9721dcd4e13d5b39d85e405784b40ec58f8ffc72fa2f80606856195e1d4deb">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xdba09041f5a95deb2aebe733f964aebed1d3719fcf77120b5776ba87f4da5020">submitCap</a></td></tr><tr><td>2026-03-18</td><td>ETH Yield</td><td>Onboard Origin ARM markets (<a href="https://app.morpho.org/ethereum/variable/0x2049bea9dfae8189895616ff4bf229c9c664e266b29c9ffe692b100be4879714/arm-weth-steth-weth">stETH</a> &#x26; <a href="https://app.morpho.org/ethereum/variable/0x883b22b724b422729ec0688e6ed71da1c5105ac6610810624f25263d7c2e7bf3/arm-weth-eeth-weth">eETH</a>)</td><td>ARM-WETH-stETH and ARM-WETH-eETH.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0xf1b13b140da916e67adfcd022236b8fee8b2c11f5bb61c28a76f51fb9480f7d8">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0x4ab3bf1d50f92e88840c63eec6d381e81222a21a94a8a5f8e6dd20afa0e2426d">submitCap</a>, acceptCap completed 2026-03-25: <a href="https://etherscan.io/tx/0x63255633d073f1441efa6edf0aae0610cdd916530ed1d7b764be283b8ed5b509">stETH</a>, <a href="https://etherscan.io/tx/0xfa3f8cbbbd5374e00b3774fd735d4732d3be42eeade8ff1eef8d74c40fd6ecad">eETH</a></td></tr><tr><td>2026-03-18</td><td>USDT Prime</td><td>Onboarded new markets</td><td>syrupUSDT &#x26; weETH</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x2f4c465d4aff251d9232f2840ad0968d8da353fabdd1f36049600733df6339e9">submitCap</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0x834e1c1ea40173B82106F9177646b66d96AE7De8&#x26;id=multisig_0x834e1c1ea40173B82106F9177646b66d96AE7De8_0x573ef57d6913d73056594bb75cfe1c56b0704ebc1754f73670302c26d3333ce6">submitCap</a></td></tr><tr><td>2026-03-18</td><td>USDC Yield (Arbitrum)</td><td>Rolled over PT-thBILL to new maturity</td><td>PT-thBILL-18JUN2026</td><td><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x297aead9c9a3c0c028cc973750d7339558e3d7285b26b6b8ce57636a5e520262">setSupplyQueue</a>,<br><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0xef50e3771170ef39e48b964fe6056c6fe0c93c25415902127d368e9479e89f07">submitCap</a></td></tr><tr><td>2026-03-09</td><td>USDC Yield</td><td>Onboard 7 new markets</td><td>Add new markets to improve yield.</td><td><a href="https://etherscan.io/tx/0x7cc4d0881f662f2f4e02c9fdf130f1da2cfe4d2e1504268a93233317ccce7d3c">submitCap</a>, <a href="https://etherscan.io/tx/0x660119353e3e379e34b9d4571d8487a91d90a567436249164d9a3477a92251cf">acceptCap</a></td></tr><tr><td>2026-03-04</td><td>USDC Yield</td><td>Offboard 4 underperforming markets</td><td>PT-stcUSD-23JUL2026,<br>PT-RLP-9APR2026,<br>liUSD-1w,<br>wbravUSDC</td><td><a href="https://etherscan.io/tx/0xae5a2d9818e72a838955274e0b3f6f464643de7857df4b7c0180b46ff9cb4796">submitCap</a>,<br><a href="https://etherscan.io/tx/0x1c27d2b1c14ab68abc7e75127a9cbe20bcc06f6d153a0797548166a7fb44ea87">submitCap</a>,<br><a href="https://etherscan.io/tx/0xd3400fad0618569ee35c602f41a691cf3c90f2ffa8984fcbd7e7747f13b3984e#eventlog">submitCap</a>,<br><a href="https://etherscan.io/tx/0xf10fa38e40793576e1bb82dbb6c81822652b11e6737ccd6df30e4e5be877d26b#eventlog">submitCap</a>,</td></tr><tr><td>2026-03-02</td><td>USDC Yield</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0xacc49fbf58feb1ac971acce68f8adc177c43682d6a7087bbd4991a05cb7a2c67/srroyusdc-usdc">srRoyUSDC</a> market</td><td>Add new market to improve yield.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x5d47e2185d2c870952eacc7f38e438f0842672ef750826f2b8e2b4f66b14f806">submitCap</a>, <a href="https://etherscan.io/tx/0xfb79a769e80a5cb4f1c3f8d86bb0497d007e51a4038f0efdcde3e0e39ff6b6bf">acceptCap</a></td></tr><tr><td>2026-03</td><td>All vaults</td><td>Upgrade Roles module to new unwrapping contract</td><td>Improve operational security.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x733a3ae6a183b256326704e7ba7a48e7796299ec936dfda562f18b5c0d757c5a">1</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075&#x26;id=multisig_0xE5aEC7D0E795456F90ceBefBa56470F0E5dfC075_0x67b24c67ecbbe2a968495d90618fbd5fc31bf61e1383a0f4f0d002fe783515e1">2</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x5226cf2bb6d6c1843bce1a34e9e5218b8ce6381b5f6fbcc06e5a9c4619765cd7">3</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x0e6a2c7fde1ff03e87c3b5e61a6f674c15acb5f6792365ec08b2b69d8c7fc87f">4</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xd15f11B334e1e233127302E5F759C17DA1260df5&#x26;id=multisig_0xd15f11B334e1e233127302E5F759C17DA1260df5_0x3d974560038f5f180fde945532d3dfec3719727088310065d6975954a3c6187f">5</a>, <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0xb5136e5bb0b03098057bc43e94717f234b9d4e5b99a4a092df096888ef815f8a">6</a></td></tr><tr><td>2026-02-20</td><td>USDC Yield &#x26; USDC Yield (Arbitrum)</td><td>Removed matured Pendle markets.</td><td>PT-iUSD-19FEB2026 (Ethereum) and PT-thBILL-27FEB2026 (Arbitrum) matured — offboard to reduce risk.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x4853ff84aa9ba77b6a8817abde4242e9239ba6dfe77ec2b85b21ea4327db7053">submitCap</a>, <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=module_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_ifda53b10dbde78287afb87992daa56367bb5ba8a898ac629ac0256e5ebef2fca29">submitCap</a></td></tr><tr><td>2026-02-19</td><td>ETH Yield</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0x66c19b7c49ce674e964eb917d117d987b693f508f2c1e3f71ac914454ed42e13/saveth-weth?subTab=advanced">savETH</a> market</td><td>Deprecate old savETH market, after migrating liquidity to newer market with better oracle design.</td><td><a href="https://etherscan.io/tx/0x643c6c11ed4a5fdca34c3dba96b89972c01a68fe814158f4128066166ae9134f">submitCap, reallocate</a></td></tr><tr><td>2026-02-15</td><td>USDC Yield (Arb)</td><td>Re-enable <a href="https://app.morpho.org/arbitrum/variable/0xd09404e9512e1341321c8ae3bd663fab7087582142ac61486635a6c072c2af12/weeth-usdc">weETH</a> market</td><td>Re-enabled market now that liquidity has improved.</td><td><a href="https://arbiscan.io/tx/0xe3e082270a996c3917a86bbe41c0905ce1d0aa71f65bacc6a4b2113a31c07eec">submitCap</a>, <a href="https://arbiscan.io/tx/0x86cc591efd81166c6d713bfc7cb01702cc3c8d9a62f2148a2a980c2864087898">acceptCap</a></td></tr><tr><td>2026-02-15</td><td>USDC Yield</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0x973e9dd45799efe8775417bcc420a3ab84a583587b2108985746e2fe201d0c83/yfi-usdc">YFI</a> market</td><td>Add new market to improve yield.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x7E43Df1c1c5A2245858b60D4655fDA83704e4171&#x26;id=multisig_0x7E43Df1c1c5A2245858b60D4655fDA83704e4171_0x232694b1c823c0b54b5801df0077493ea956dd4740360bd46d311c7e6fc5cea9">submitCap</a>, <a href="https://etherscan.io/tx/0x894cfcac40bf075182f799af3ce75d3503ce90a6f2269a1981b5312edd34f646">acceptCap</a></td></tr><tr><td>2026-02-10</td><td>ETH Prime</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0x66c19b7c49ce674e964eb917d117d987b693f508f2c1e3f71ac914454ed42e13/saveth-weth?subTab=advanced">savETH</a> market</td><td>Deprecate old savETH market, after migrating liquidity to newer market with better oracle design.</td><td><a href="https://etherscan.io/tx/0x79981b9fc9a003414f27ab36d8adf22f95449adc5c8a692fb9ad2933598d9789">submitCap, reallocate</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xdb573666cb614da8216f16f6602664361edfae09b991933289af6ee17cc065e7">updateWithdrawQueue</a></td></tr><tr><td>2026-02-02</td><td>ETH Prime</td><td>Remove wstETH market</td><td>Removal of un-used market due to low liquidity.</td><td><a href="https://app.safe.global/transactions/msg?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;messageHash=0x6aafa122a5bdcb84ff424a2b27b0ef1843236fea427da6c03aff7af58afc704c">permitSingle</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x8444621ffcec0c2502be196f58471e00040137318713c154db48aa396f2dce29">reallocate</a>,<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xd65c7d34a6ad04f4ca43212f88a7846485eb6911fad7519c72ec714ea84c0caa">updateWithdrawQueue</a></td></tr><tr><td>2026-01-30</td><td>USDC Yield (Arb)</td><td>Removed matured <a href="https://app.morpho.org/arbitrum/variable/0x84367e5bc5df26437618f749d5beb793746e620560721fec15518d684191ebc6/pt-syrupusdc-29jan2026-usdc">PT-syrupUSDC-29JAN2026</a> market</td><td>Offboard matured Pendle PT.</td><td><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x1c852ec7f671ae184373a571e18eaa022d6138ab66cb0416889a185d2b49e629">submitCap</a></td></tr><tr><td>2026-01-26</td><td>ETH Prime</td><td>Enable <a href="https://app.morpho.org/ethereum/variable/0xd98cd88ae5b336086b39fb1d62ba6171282e946105b010143f0e89f8fe7cff36/saveth-weth">savETH</a> market.</td><td>Additional yield source: market deployed by KPK that has a fundamental exchange rate oracle.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x3bade5143cdd74f9e66acbb416476c96074e745ab9b7c1dba441ad5313f749ab">submitcap</a>, <a href="https://etherscan.io/tx/0x93664340bbdda131a113f791da911028dbb7ad2ecedf309a3aef2272c4bc8beb">acceptcap</a></td></tr><tr><td>2026-01-20</td><td>ETH Prime</td><td>Re-enabling <a href="https://app.morpho.org/ethereum/variable/0xba761af4134efb0855adfba638945f454f0a704af11fc93439e20c7c5ebab942/rseth-weth">rsETH</a> market.</td><td>Re-add market to improve yield, now risk has been assessed.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xc8c06429249021df99fe3a97082fa4c15ed778725743b12aa73ebc0d30e29dd3">submitCap</a><br><a href="https://etherscan.io/tx/0x93664340bbdda131a113f791da911028dbb7ad2ecedf309a3aef2272c4bc8beb">acceptCap</a></td></tr><tr><td>2026-01-20</td><td>USDC Prime</td><td>Onboard <a href="https://app.morpho.org/ethereum/variable/0xb8fef900b383db2dbbf4458c7f46acf5b140f26d603a6d1829963f241b82510e/oeth-usdc">OETH</a> market.</td><td>Add new market to improve yield.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0x14456096987b04c36c18d1fd163fd6ad92a5d19955530768799b5ffe08072dd9">submitCap</a><br><a href="https://etherscan.io/tx/0xebfd62ad83032e999ffad466915c7ca9589f4f8e2375954f2e414c0d5d100ede">acceptCap</a></td></tr><tr><td>2026-01-20</td><td>ETH Prime</td><td>Offboard <a href="https://app.morpho.org/ethereum/variable/0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41/wsteth-weth">wstETH</a> and temporarily offboard <a href="https://app.morpho.org/ethereum/variable/0xba761af4134efb0855adfba638945f454f0a704af11fc93439e20c7c5ebab942/rseth-weth">rsETH</a> market.</td><td>Avoid low liquidity, and precautionary measure due to risk alert, respectively.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x84df9f32b989c028b2916a87126127cd0252573427c2b37b380a8f510e1fb0db">reallocate</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xea0b3f0f2fc246dc9562412e3685735a414036a2e410e2428af7e82a4265c8ed">submitCap</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x49a08bfdcb311a0a81a004b663acd964d0f5ab8ece142bc49ea27a5834708910">reallocate</a></td></tr><tr><td>2026-01-13</td><td>ETH Prime</td><td>Add <a href="https://app.morpho.org/ethereum/variable/0x138eec0e4a1937eb92ebc70043ed539661dd7ed5a89fb92a720b341650288a40/wbtc-weth">WBTC</a> market &#x26; remove <a href="https://app.morpho.org/ethereum/variable/0xa0534c78620867b7c8706e3b6df9e69a2bc67c783281b7a77e034ed75cee012e/ezeth-weth?tab=market">ezETH</a> market</td><td>Adjust market exposure as liquidity conditions changed.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x5838360f36ebe5d3d69a6274beecb58d14771f6dfd8a344f962e264c5ed469cc">acceptCap</a> (WBTC)<br><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x039c1605381233f9dfcd56810fd17937079a74ccfcd1738901777d24d9de6c14">submitCap</a> (ezETH)</td></tr><tr><td>2026-01-02</td><td>USDC Yield Arbitrum</td><td>Add <a href="https://app.morpho.org/arbitrum/variable/0xc7670063349ac19dfa324ead7bd7da2985ae931e1b09fb0e31b62c6486b730bd/rlp-usdc">RLP</a>, <a href="https://app.morpho.org/arbitrum/variable/0x551dbcdcceaf9322986e0cddde993d49840522a9532dc441359acd98af8badff/thbill-usdc">thBILL</a>, <a href="https://app.morpho.org/arbitrum/variable/0x6c831dcc45a7c0af00b751da651bd874b96653c587615d11aafade7b357c4b43/pt-thbill-19feb2026-usdc">PT-thBILL</a> markets.</td><td>Onboard new yield sources.</td><td><a href="https://arbiscan.io/tx/0xdd78b561f8f0761738454748baad1c259bbdfe1c1ac1d1121b55073a661e19de#eventlog">RLP</a>, <a href="https://arbiscan.io/tx/0x7fcb687044bd72ea09dbf17e23cb59eacb79687c4a4418eb352e6fc0320b7e41#eventlog">thBILL</a>, <a href="https://arbiscan.io/tx/0x5f45bc3e41165403a2bd0bc9d021b4b24a4bab7afef81c8b0be1c24e5adaf1b5">PT-thBILL</a> acceptCap</td></tr><tr><td>2025-12-12</td><td>ETH Prime</td><td>Add <a href="https://app.morpho.org/ethereum/variable/0x66c19b7c49ce674e964eb917d117d987b693f508f2c1e3f71ac914454ed42e13/saveth-weth?subTab=advanced">savETH/ETH</a>, remove <a href="https://app.morpho.org/ethereum/variable/0x138eec0e4a1937eb92ebc70043ed539661dd7ed5a89fb92a720b341650288a40/wbtc-wethwbtc">WBTC/ETH</a> and <a href="https://app.morpho.org/ethereum/variable/0x2cbfb38723a8d9a2ad1607015591a78cfe3a5949561b39bde42c242b22874ec0/cbbtc-weth">cbBTC/ETH</a> markets.</td><td>Onboard an additional yield source, while offboarding two markets with suboptimal yield and liquidity.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xe037c31cc75f013453f0301afc9f14db6bb06ac3136418bbe77ff80541f604bb">acceptCap</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xed1e8fbf8a652f06a146d074b09c627ee763d3fbc83a1fb46ebf7220ec3fb338">submitCap</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x6b6691f5a5161be0ca3c98d61ed5bb32ce72eee4aeb96cd016a1424c97cde330">submitCap</a></td></tr><tr><td>2025-12-12</td><td>USDC Yield Arbitrum</td><td>Removed <a href="https://app.morpho.org/arbitrum/variable/0xd09404e9512e1341321c8ae3bd663fab7087582142ac61486635a6c072c2af12/weeth-usdc">weETH/USDC</a> and <a href="https://app.morpho.org/arbitrum/variable/0x7e7c08f2d8bb6821408ac6b4e2f322d73ba1fb8ce20b3b65087f440ef3b2f8f1/susde-usdc">sUSDe/USDC</a> markets.</td><td>Suboptimal liquidity in both Tier C markets. Remove to minimise risk.</td><td><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x1b87d0e63a4ee879fabe81c191860e96e9358eb3a610e2beae9add7ff0e39e06">submitCap</a>, <a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0x1b4a713518d4d8b779f70f2de9d0b3bf4b9b0b1ed3851a24442d623ba251fd9d">submitCap</a></td></tr><tr><td>2025-12-04</td><td>USDC Prime</td><td>Remove <a href="https://app.morpho.org/ethereum/variable/0xdb8938f97571aeab0deb0c34cf7e6278cff969538f49eebe6f4fc75a9a111293/eth-usdc?subTab=advanced">ETH+/USDC</a> market.</td><td>Liquidity of this market has deteriorated. Remove to minimise risk.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xf8182E5827c06a47A985eC565A3bcd56437a97bE&#x26;id=multisig_0xf8182E5827c06a47A985eC565A3bcd56437a97bE_0xb6e9d0b2338fc5bd74b362280d3aaf8c1e87b77af551fa7cdde9b067fcfabe2e">Tx details</a></td></tr><tr><td>2025-12-03</td><td>USDC Yield Arbitrum</td><td>Add <a href="https://app.morpho.org/arbitrum/variable/0x77fe2f7c2dd6f4da6bc5f445b06052ff8df55cb70cfce9afc16ec3c69a5fd3a3/susds-usdc?subTab=advanced">sUSDS/USDC market</a>.</td><td>Liquidity of this market has improved substantially, providing a solid additional yield source.</td><td><a href="https://app.safe.global/transactions/tx?safe=arb1:0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d&#x26;id=multisig_0xE8bED28828f4DD93FB98232F8e85c8880D1f7e1d_0xa703bc0a3caa4ce40cb4dec87bac6f35c4ba3834891399d255f2748b86df2336">submitCap</a>, <a href="https://arbiscan.io/tx/0x862229c952f8b59fc5e84bd7848ffb05477682ccdee73573073e909754be6fce">acceptCap</a></td></tr><tr><td>2025-11-21</td><td>ETH Prime</td><td>Add <a href="https://app.morpho.org/ethereum/variable/0xa0534c78620867b7c8706e3b6df9e69a2bc67c783281b7a77e034ed75cee012e/ezeth-weth">ezETH</a> &#x26; <a href="https://app.morpho.org/ethereum/variable/0xba761af4134efb0855adfba638945f454f0a704af11fc93439e20c7c5ebab942/rseth-weth">rsETH</a> markets, accept Guardian.</td><td>Adds new markets to improve yield and sets Guardian role to the Risk Council Safe.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0xdb6b2a744c37e7a42d935ea1f739274fbedb342adc33e35280a00f30db06e7f1">New markets</a>, <a href="https://app.safe.global/transactions/tx?safe=eth:0xC266b1181A80E84EDC2c6596718E88E8115c1eaa&#x26;id=multisig_0xC266b1181A80E84EDC2c6596718E88E8115c1eaa_0x7e978dc4d56e7d46c6411729731b38254d06f071b8c8e61c2f6b56e66b81fed8">Guardian</a></td></tr></tbody></table>


# Symbiotic

KPK vaults on Symbiotic

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC RWA Liquidity</strong></td><td>Ethereum</td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="/vaults/vaults/symbiotic/ethereum/usdc-rwa-liquidity">USDC RWA Liquidity</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr></tbody></table>

### Protocol overview

KPK curates on [Symbiotic](https://symbiotic.fi/) V2, the protocol's modular vault infrastructure for curated onchain strategies. Each vault accepts a single deposit asset and allocates it across approved adapters, each of which connects to an external venue or mechanism. KPK's focus is [Liquid Lane](https://docs.symbiotic.fi/liquid-lane), Symbiotic's mechanism for providing instant withdrawal liquidity on tokenised RWAs, combined with USDC lending adapters that include KPK-curated Morpho vaults.

This is not restaking. KPK's vault does not use Symbiotic's earlier restaking and shared-security stack, and deposits are never used to secure networks or exposed to slashing.

### How Liquid Lane works

Liquid Lane addresses a structural gap in tokenised-RWA markets. RWAs settle on the issuer's schedule, typically T+1 or longer, and a holder who wants out sooner has no onchain venue deep enough to sell into, making building secondary liquidity for every individual RWA expensive for issuers and fragmenting the liquidity that exists.

Liquid Lane closes that gap with a request-for-quote engine. A curator states in advance the discount at which it is willing to acquire a given asset. When a holder needs immediate liquidity, the vault pays out at that discount, and the asset is routed straight into a dedicated per-asset redemption account, in the same transaction. The spread between the discounted purchase price and the redemption price at the issuer's NAV is the vault's return and the holder's cost of exiting early.

The RWA never rests in the vault. What the vault carries between the swap and the issuer settlement is a redemption claim, not the token, and there is currently no option to hold an acquired asset, sell it over the counter, or auction it. Every acquisition is redeemed natively with the issuer. The vault cannot build a discretionary position in the underlying asset, and its capital is committed for the duration of the issuer's settlement cycle. The minimum discount is what compensates for that.

Each acquisition is bounded by two onchain limits set per asset by the curator: a minimum discount enforced inside the swap, and an acquisition limit on how much unsettled exposure the vault can carry in that asset. The RFQ engine is interface-compatible with Uniswap X and CoW Swap, so requests reach the vault through the venues holders already use.

### Curator model

Symbiotic uses a curator model: curators decide which venues and assets a vault may use and set the limits that apply to each. KPK acts as curator, defining safe operating conditions and reallocating liquidity when conditions change. All decisions follow the [Risk Framework](/vaults/infrastructure/risk-framework), which governs eligibility criteria, risk-tiering, and monitoring cadence.

Curation on a Liquid Lane vault covers four things:

1. **Asset eligibility and acquisition terms.** Only assets that pass due diligence are added to the Liquid Lane adapter. Each carries a per-asset acquisition limit and a minimum discount, which together bound how much unsettled exposure the vault can carry in any one asset and how much compensation it requires for carrying it.
2. **How requests are filled.** Fills execute against quotes signed by the curator side, with the minimum discount and the acquisition limit enforced onchain against every fill. A standing quote settles matching requests automatically, which suits assets whose price moves slowly and predictably; for assets where a large single-day move could make an automatic fill mispriced, quotes can be signed per order instead, keeping each fill under individual review. The quoting mode is a per-asset curation choice, made alongside the limit and the discount.
3. **Idle capital allocation.** Capital not committed to an RWA acquisition is allocated to lending adapters, so the vault yields continuously rather than waiting in cash for a request that may not arrive.
4. **Withdrawal liquidity.** Because a redemption claim cannot be recalled before the issuer settles, the share of the vault sitting in lending venues that can be exited on demand is what backs depositor withdrawals. KPK manages that share directly through adapter limits.

### Vault architecture

A Symbiotic V2 vault separates depositor accounting from allocation:

* **Vault** holds depositor assets, issues shares, and applies fees. Deposits and withdrawals happen here.
* **Delegator** holds the ordered list of adapters and the limits on each. It is owned by the vault.
* **Adapters** connect to a single external venue or mechanism. KPK's vault runs Morpho Vault V2 adapters, an Aave V3 adapter, and the Liquid Lane adapter.

Vault and adapter contracts sit behind a `MigratableEntityProxy`, Symbiotic's versioned upgrade pattern. A migration is triggered through the Symbiotic vault factory, which permits it only from the contract's `owner()` and only to an implementation version the factory has already whitelisted above the current one. This is not a general upgrade path: the owner selects among Symbiotic-published versions rather than supplying its own code.

### Allocation and auto-allocate

Each adapter carries two limits: an **absolute limit** in deposit-token units and a **relative limit** as a share of vault assets. The binding constraint is whichever is lower.

Allocation runs through Symbiotic's auto-allocate routine, which fills adapters in list order: the first adapter is filled to its limit, then the next, and so on. The routine can be called on each deposit or on a fixed cadence, which means the standing allocation follows from the queue order and the caps, rather than from a separate allocation engine. The caps are maxima, not targets: within them, a curator can move the actual allocation at any time through a rebalance. KPK's rebalancing agent calls the routine, and can also allocate and deallocate directly within its scoped permissions.

Withdrawals are served from the vault's own balance and from what can be pulled back out of the lending adapters. Capital committed to an outstanding redemption claim cannot be recalled until the issuer settles. Anything beyond the immediately available amount goes through a withdrawal-request queue: a depositor submits a request, it is filled as liquidity becomes available, and the depositor then claims it. The gap between an issuer's settlement cycle and a depositor's expected withdrawal speed is the duration mismatch a curator manages, which is why the share of the vault held in instantly withdrawable venues is an explicit curation decision, documented per vault.

### Governance and control

Authority is split so that actions which can expand risk move slowly, and actions needed during an incident move fast.

#### Roles

<table><thead><tr><th width="330">Action</th><th>Held by</th></tr></thead><tbody><tr><td>Vault and delegator admin</td><td>Security Council</td></tr><tr><td>Add adapter</td><td>Security Council</td></tr><tr><td>Set deposit limit, deposit whitelist, fees</td><td>Security Council</td></tr><tr><td>Allocate, deallocate, force-deallocate</td><td>Curator Safe</td></tr><tr><td>Swap adapters</td><td>Curator Safe</td></tr><tr><td>Set adapter limits</td><td>Curator Safe</td></tr><tr><td>Set auto-allocate route</td><td>Curator Safe</td></tr><tr><td>Set per-asset limit and minimum discount</td><td>Curator Safe</td></tr><tr><td>Pause and unpause Liquid Lane adapter</td><td>Curator Safe</td></tr><tr><td>Migrate vault implementation</td><td>Security Council (vault <code>owner()</code>)</td></tr></tbody></table>

* [**Security Council**](https://app.safe.global/settings/setup?safe=eth:0x354C92aF243d53A24feb3dFF20372Af7b7c47478) (5/8) is the admin on the vault and delegator. On KPK's vault it also holds `owner()`, a separate authority whose scope is transferring or renouncing ownership and triggering a version migration through the factory; it reaches no economic parameter.
* **Curator Safe** (2/5) holds the operational roles. The Security Council is one of its signers, and can grant itself any operational role directly because it is the admin.
* **Agents** hold narrowly scoped permissions through KPK's [Permissions Layer](https://kpk.io/blog/permissions-layer), limited to specific functions on specific contracts.

Expanding the venues a vault can reach is a slower decision than adjusting terms within venues already approved, which is why adding an adapter sits above the Curator Safe. Pause and unpause on the Liquid Lane adapter each resolve to a single address, so both sit with the Curator Safe and there is no single-signature pause path.

Liquid Lane ships without a built-in timelock on its economic parameters. KPK's position is that separating authority by speed is the stronger control here, and that delaying the operational lane would slow incident response without removing the underlying authority. Changes through the Curator Safe are bounded by its 2/5 threshold, the Security Council's seat as a signer, and every parameter change being recorded in the [Changelog page](https://docs.kpk.io/changelog/).

### Vault configuration

Each vault page documents the deposit asset, contract addresses, the Safes with their signers and thresholds, and enabled strategies.

* [Ethereum](/vaults/vaults/symbiotic/ethereum)
  * [USDC RWA Liquidity](/vaults/vaults/symbiotic/ethereum/usdc-rwa-liquidity)


# Ethereum

KPK-curated Symbiotic vaults on Ethereum.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>KPK USDC RWA Liquidity</strong></td><td>Ethereum</td><td><a href="/vaults/vaults/symbiotic/ethereum/usdc-rwa-liquidity">USDC RWA Liquidity</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FskL2KuxH4AOMfwOViYgL%2FMorpho_USDC_Yield_L.png?alt=media&amp;token=e81618a4-f241-424e-b2d7-0edc2ab593aa">Morpho_USDC_Yield_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdHHN4PCmvk2jXYnAY7sI%2FMorpho_USDC_Yield_D.png?alt=media&amp;token=5aabb1cd-5370-4782-a570-afc2bb98a9eb">Morpho_USDC_Yield_D.png</a></td></tr></tbody></table>


# USDC RWA Liquidity

The USDC RWA Liquidity vault earns a liquidity premium by providing exit liquidity on tokenised RWAs: buying at a discount when holders need immediate liquidity, and redeeming at NAV with the issuer. Idle capital is allocated to USDC lending strategies, including KPK-curated Morpho vaults, so capital remains continuously yielding. RWA exposure is capped per asset, with **24/7 automated monitoring**.\
\
The vault delivers <mark style="color:$primary;">**RWA-linked returns without RWA duration**</mark>, since every acquisition is redeemed with the issuer rather than held.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Deposit</td><td><a href="https://app.kpk.io/vaults/symbiotic-usdc-rwa-liquidity-mainnet">https://app.kpk.io/vaults/symbiotic-usdc-rwa-liquidity-mainnet</a></td></tr><tr><td>Live metrics</td><td><a href="https://dune.com/kpk/kpk-usdc-rwa-liquidity">https://dune.com/kpk/kpk-usdc-rwa-liquidity</a></td></tr></tbody></table>

### Key information

<table data-header-hidden><thead><tr><th width="239.86328125"></th><th></th></tr></thead><tbody><tr><td><strong>Chain</strong></td><td>Ethereum</td></tr><tr><td><strong>Protocol</strong></td><td>Symbiotic</td></tr><tr><td><strong>Standard</strong></td><td><a href="https://ethereum.org/developers/docs/standards/tokens/erc-4626/">ERC-4626</a></td></tr><tr><td><strong>Token</strong></td><td>USDC</td></tr><tr><td><strong>Address</strong></td><td><a href="https://etherscan.io/address/0x8BcD746976885b5832bAD07B4921E3f2dD1D3703">0x8BcD746976885b5832bAD07B4921E3f2dD1D3703</a></td></tr><tr><td><strong>Liquidity target</strong></td><td>≥50%</td></tr><tr><td><strong>Fees</strong></td><td>10% on performance, 0% on management</td></tr><tr><td><strong>Delegator</strong></td><td><a href="https://etherscan.io/address/0xdC94E7D2eFaF84e620C8d39386F168aAa99da058">0xdC94E7D2eFaF84e620C8d39386F168aAa99da058</a></td></tr></tbody></table>

{% hint style="info" %}
The vault's onchain name and symbol, `KPK USDC LiquidLane` and `KPK_USDC_LL`, are immutable and were set at deployment. `KPK USDC RWA Liquidity` is the product name used across Symbiotic's interfaces and KPK's documentation. Both refer to the same contract.
{% endhint %}

### Strategy

The vault runs two return streams over one pool of USDC. Yield on the lending leg comes from the underlying venues' supply rates, with Symbiotic network rewards accruing to vault capital in addition. On an RWA acquisition, the discount captured accrues to the vault, with the curator's share taken through the performance fee.

**Lending.** Capital not committed to an RWA acquisition is allocated across USDC lending adapters. Today, that is three KPK-curated Morpho vaults. KPK manages the vault, so this leg holds the majority of assets per the ≥50% withdrawable-liquidity target, and it backs depositor withdrawals. The lending leg doubles as the vault's liquidity sleeve, with positions recallable on demand, subject to the underlying markets' available liquidity.

**RWA acquisition.** A holder of an approved tokenised RWA who wants out before the issuer settles submits a request through Liquid Lane. These holders are third parties selling to the vault, not depositors in it. When a request meets the vault's terms, the vault pays the holder USDC at a discount through the Liquid Lane adapter. The asset is then forwarded into a dedicated per-asset redemption account in the same transaction, and the claim is redeemed with the issuer at its published NAV on the issuer's settlement schedule. The discount compensates for the settlement window, including NAV movement within it.

The RWA never rests in the vault: what the vault carries between the swap and settlement is a redemption claim rather than the token, so it cannot build a discretionary position in the underlying. Native redemption is the only route available today, and each acquisition is subject to a per-asset minimum discount and a cap on outstanding, unsettled redemptions. See [**how Liquid Lane works**](/vaults/vaults/symbiotic#how-liquid-lane-works) for the mechanism and both controls in full.

Allocation runs through a deposit queue. Free assets fill the adapters sequentially, each to the lower of its relative and absolute caps, before the next adapter receives anything; the relative caps shown are the binding constraint today. The first three adapters form the queue, in order, and Liquid Lane is whitelisted but sits outside it. Risk-tiering lives with what each adapter holds: the per-market tiers behind the Morpho vaults are published on their vault pages, and the RWA assets are tiered in the [approved asset set](#approved-asset-set) below.

<table><thead><tr><th width="170">Adapter</th><th width="190">Allocates to</th><th width="120">Curator</th><th align="right">Allocation cap</th></tr></thead><tbody><tr><td><a href="https://etherscan.io/address/0x70849108e817436b9e1e51F91A106E37D19cBc47">Morpho Vault V2</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-prime">KPK USDC Prime</a></td><td>KPK</td><td align="right">10%</td></tr><tr><td><a href="https://etherscan.io/address/0xF1484A59740018c1F25487c1EFF95e2502da304f">Morpho Vault V2</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield">KPK USDC Yield</a></td><td>KPK</td><td align="right">40%</td></tr><tr><td><a href="https://etherscan.io/address/0xE075f215684CE141c426301991535817B8e6C373">Morpho Vault V2</a></td><td><a href="/vaults/vaults/morpho/ethereum/usdc-yield-rwa">KPK USDC Yield RWA</a></td><td>KPK</td><td align="right">50%</td></tr><tr><td><a href="https://etherscan.io/address/0x97C0BAefCF688A8389108a0893144ae0a1408B3a">Liquid Lane</a></td><td><a href="#approved-asset-set">Approved asset set</a></td><td>KPK</td><td align="right">20%</td></tr></tbody></table>

Incoming deposits are filled into KPK USDC Prime first. Prime targets blue-chip markets with instantly withdrawable liquidity, so the queue builds the most liquid position first. Once Prime holds 10% of the vault, further deposits flow to the higher-yielding KPK USDC Yield, up to 40%. Beyond that, deposits flow to KPK USDC Yield RWA, up to 50%. The caps are maxima, not target allocations; within them, KPK can adjust the actual allocation at any time via a rebalance.

The Liquid Lane adapter sits entirely outside the deposit queue. No deposit flows into it automatically: capital enters only when KPK fills an approved acquisition. Its 20% cap limits how much of the vault can be held in outstanding RWA redemption claims at any time; it does not route deposits to it. The vault also does not make bridge loans. Only the four adapters above are whitelisted, and any future addition would appear here and in the [changelog](https://docs.kpk.io/changelog/) first.

Withdrawals are served from the vault balance plus what the lending leg can release on demand. The measure KPK manages against the ≥50% withdrawable-liquidity target is the vault's atomic liquidity: what sits in the vault plus what can be pulled from the underlying Morpho vaults on demand. A withdrawal larger than that falls back to a withdrawal-request queue, filled as liquidity frees up and then claimed by the depositor. With no RWA acquisitions filled yet, no capital is locked in redemption claims today; the queue still applies to any withdrawal larger than what the lending markets can release at that moment.

Vault management runs through agents operated by KPK, executing whitelisted functions through KPK's [Permissions Layer](https://kpk.io/blog/permissions-layer). Allocation and deallocation are automated today, with the exit and replenish agents still to be configured. RWA acquisition does not run through the agents; requests are filled against the pre-set per-asset terms, which only the Curator Safe can change. For scoped permissions and the current rollout state, see the Governance and controls section below.

### Risk framework

This vault follows KPK's [**Risk Framework**](/vaults/infrastructure/risk-framework) for asset selection, tiering, and ongoing monitoring. Assets are assessed both as assets and as postures: carrying a redemption claim through to settlement entails a different set of risks than lending against the same asset as collateral, so an asset approved as collateral elsewhere is not automatically approved for acquisition here. The decisive question for this vault is redemption reliability, meaning whether the issuer settles claims of the size the vault would take, on the schedule it publishes. Because holding is not an option for the vault, collateral value and liquidation mechanics matter less here than the integrity of the redemption path itself.

#### Acquisition framework

The RWA leg does not run on discretion. Before an asset can be acquired at all, it undergoes due diligence under the Risk Framework, receives a risk tier, an acquisition limit, and a minimum discount, and completes onboarding with its issuer so the redemption path is tested end-to-end. Once an asset is enabled, every individual fill still has to clear three tests at once:

* **Economics.** The discount must lift the portfolio's net yield above what the same capital earns in the lending leg. The KPK-curated Morpho vaults are the hurdle rate: a fill that does not beat them over the settlement window is declined.
* **Exposure.** The fill must fit within the asset's acquisition limit, which caps outstanding unsettled redemptions per asset. Limits follow the asset's risk tier, which weighs the issuer's settlement cycle and redemption cooldown, NAV volatility, the composition of the underlying portfolio, and the legal enforceability of the redemption claim.
* **Liquidity.** The vault's withdrawable-liquidity target of ≥50% must hold after the fill, measured as atomic liquidity: the vault balance plus what the lending leg can release on demand.

An acquisition is an optimisation across yield, liquidity, and exposure under those constraints, not a directional view on any asset. The parameters are public; the calibration between them is KPK's curation. The controls are ex-ante and enforced onchain: the minimum discount and the acquisition limit bind every fill inside the swap, including one signed by the curator itself. A fill also requires a quote signed by an account the adapter authorises, so whether an asset fills automatically is a quoting choice. A standing signed quote settles matching requests without per-request involvement, which suits assets whose prices move slowly; signing per order keeps each fill under individual review. KPK makes that choice per asset, alongside the tier, limit, and discount.

#### Approved asset set

Material parameter changes and their rationale are recorded in the [Changelog page](https://docs.kpk.io/changelog/).

<table><thead><tr><th width="120">Asset</th><th width="130">Issuer</th><th width="100">Risk tier</th><th width="130">Quoting mode</th><th align="right">Acquisition limit</th><th align="right">Min. discount</th></tr></thead><tbody><tr><td><a href="https://etherscan.io/address/0x8c213ee79581Ff4984583C6a801e5263418C4b86">JTRSY</a></td><td>Centrifuge</td><td>B</td><td>Standing</td><td align="right">200,000</td><td align="right">0.05%</td></tr><tr><td><a href="https://etherscan.io/address/0xA6233014B9b7aaa74f38fa1977ffC7A89642dC72">deJTRSY</a></td><td>Centrifuge</td><td>B</td><td>Pending</td><td align="right">0</td><td align="right">0.05%</td></tr><tr><td><a href="https://etherscan.io/address/0x5a0F93D040De44e78F251b03c43be9CF317Dcf64">JAAA</a></td><td>Centrifuge</td><td>C</td><td>Standing</td><td align="right">150,000</td><td align="right">0.05%</td></tr><tr><td><a href="https://etherscan.io/address/0xAAA0008C8CF3A7Dca931adaF04336A5D808C82Cc">deJAAA</a></td><td>Centrifuge</td><td>C</td><td>Pending</td><td align="right">0</td><td align="right">0.05%</td></tr><tr><td><a href="https://etherscan.io/address/0x7433806912Eae67919e66aea853d46Fa0aef98A8">mGLOBAL</a></td><td>Midas</td><td>C</td><td>Per-order</td><td align="right">50,000</td><td align="right">3.00%</td></tr><tr><td><a href="https://etherscan.io/address/0x238a700eD6165261Cf8b2e544ba797BC11e466Ba">mF-ONE</a></td><td>Midas</td><td>D</td><td>Per-order</td><td align="right">50,000</td><td align="right">2.50%</td></tr></tbody></table>

The acquisition limit is an absolute amount per asset in USDC that caps outstanding unsettled redemptions; it is not a share of vault assets. Limits follow the risk tier, so the lowest-risk assets carry the largest allowance. The two Midas assets are the exception: they are sized jointly across KPK's total Midas exposure rather than by tier. Per-asset limits are not additive, and the Liquid Lane adapter's cap of 20% of vault assets bounds the total outstanding claims across the entire set.

Quoting mode is a signing policy rather than an onchain parameter, so this table is its public record. A Pending asset has no open redemption path with its issuer yet: the vault could take delivery but could not redeem, so its acquisition limit is set to zero until the path opens.

Settlement mechanics differ by issuer, and each asset has its own redemption account that holds the claim until it clears. Two things set how long that takes. The adapter applies a fixed cooldown before a redemption becomes executable: 12 hours for mGLOBAL, 36 hours for mF-ONE, and none for the Centrifuge assets, which instead redeem through an asynchronous redemption vault. The issuer's own settlement cycle then runs on top of that and varies per asset. It is the combined window that the minimum discount has to compensate for, which is why both the discount and the acquisition limit are set per asset rather than uniformly.

#### Key risks

* Redemption risk: issuer delay, gating, or a partial settlement leaves the claim outstanding, and capital in the RWA leg is not withdrawable on demand
* Oracle risk: acquisition and redemption both price off issuer-published NAV, so a stale or incorrect NAV misprices the trade
* Transferability risk on permissioned assets, changeable by the issuer, in some cases, without delay
* Concentration risk across a small set of issuers
* Smart-contract and dependency risk (Symbiotic V2, the Liquid Lane adapter, and the underlying lending venues)
* There is no insurance fund or reserve tranche, so a shortfall flows into the share price, although exposure is bounded per asset by the acquisition limit and the minimum discount

These risks are actively monitored and managed, but cannot be fully eliminated. See the [**Symbiotic Disclaimer**](/vaults/resources/legal/symbiotic-disclaimer).

### Governance and controls

[Critical actions follow a layered process](/vaults/vaults/symbiotic#governance-and-control) designed for transparency, security, and timely response. The [Curator Safe](https://app.safe.global/settings/setup?safe=eth:0x73af5fcdF830035401e00c322D657982b4a71288) (2/5) holds day-to-day operational roles and is\
`0x73af5fcdF830035401e00c322D657982b4a71288`.

The [Security Council](https://app.safe.global/settings/setup?safe=eth:0x354C92aF243d53A24feb3dFF20372Af7b7c47478) (5/8) `0x354C92aF243d53A24feb3dFF20372Af7b7c47478` is admin on the vault and delegator, holds vault `owner()`, and is one of the Curator Safe's five signers.

#### Agent permissions

The rebalancing agent [`0xbc901Fd01CB7f339Fd38cfa0e7E6484C199E316E`](https://etherscan.io/address/0xbc901Fd01CB7f339Fd38cfa0e7E6484C199E316E) executes through the Curator Safe's Roles module under the `symbiotic_rebalancer_agent` role key, scoped to `allocate`, `allocateExact`, `allocateAll`, `deallocate`, `deallocateExact`, `deallocateAll`, and `forceDeallocate`. It cannot acquire RWAs, change any limit or discount, add or remove an asset, or move funds outside the vault's approved adapters.

An exit agent and a replenish agent are being implemented; until they are live, the actions they cover run through the Curator Safe.


# Change log

This page records **all major configuration and parameter changes** to KPK-curated Symbiotic vaults.

Entries are listed in reverse chronological order. For current parameters, see the individual vault pages.

<table><thead><tr><th width="120.96875">Date</th><th width="124.5">Scope</th><th width="209.171875">Change</th><th width="217.24609375">Description/Rationale</th><th width="216.00390625">Tx/Reference</th></tr></thead><tbody><tr><td>2026-09-08</td><td>USDC RWA Liquidity</td><td>Set per-asset acquisition limits and minimum discounts on the Liquid Lane adapter: JTRSY 200,000 at 0.05%, JAAA 150,000 at 0.05%, mGLOBAL 50,000 at 3.00%, mF-ONE 50,000 at 2.50%. deJAAA and deJTRSY stay at a zero limit.</td><td>First enablement of the RWA leg. Discounts clear 10% APY over normal settlement and preserve capital at the worst case; limits follow the risk tier. The wrappers await issuer whitelisting.</td><td><a href="https://app.safe.global/transactions/tx?safe=eth:0x73af5fcdF830035401e00c322D657982b4a71288&#x26;id=multisig_0x73af5fcdF830035401e00c322D657982b4a71288_0xe07b678ad0fcbef533ed554dcad43ca5076c565b58a36706f7a36b737f5ab74f">setLimit + setMinDiscount</a></td></tr><tr><td>2026-08-31</td><td>USDC RWA Liquidity</td><td>Raised the Liquid Lane adapter's relative cap from 0% to 20%, via an intermediate 10%.</td><td>Gives the RWA leg headroom to carry outstanding redemption claims as acquisitions come online, ahead of any assets actually being enabled.</td><td><ul><li><a href="https://etherscan.io/tx/0x9d2aabe3a9ce6109babd73b045e0927460e2693e857b94e5c98fd217f1c23740">setLimits (0% → 10%)</a></li><li><a href="https://etherscan.io/tx/0x8c1deddaee04d63ff871970da9fcdd58175308cd71317cd78ea6f6c434b13f7d">setLimits (10% → 20%)</a></li></ul></td></tr><tr><td>2026-08-26</td><td>USDC RWA Liquidity</td><td>Added a fourth adapter routing into <a href="https://github.com/karpatkey/kpk-docs-gitbook/tree/main/vaults/morpho/ethereum/usdc-yield-rwa.md">KPK USDC Yield RWA</a> (<a href="https://etherscan.io/address/0xE075f215684CE141c426301991535817B8e6C373"><code>0xE075...C373</code></a>), reordered the deposit queue, and reset relative caps: USDC Prime unchanged at 10%, USDC Yield down from 90% to 40%, USDC Yield RWA in at 50%.</td><td>Adds a third Morpho lending leg ahead of Liquid Lane in the queue, redistributing the overflow that previously went entirely to USDC Yield.</td><td><a href="https://etherscan.io/tx/0x41d9f1b6ae2d5beda648e28ef73722c17ff9b585c8b22a0a2e9d88dbd635cc29">SwapAdapters + setLimits</a></td></tr><tr><td>2026-08-04</td><td>USDC RWA Liquidity</td><td>Revoked the deploying EOA's <code>DEFAULT_ADMIN_ROLE</code> on the vault and delegator, and transferred vault <code>owner()</code> to the Security Council.</td><td>Removes the last single-EOA authority. <code>owner()</code> is separate from the admin role and governs version migration, so both had to move.</td><td><ul><li><a href="https://etherscan.io/tx/0x51a3a1953c532b516baa06acbd88a0af11681d45d8bbc2ca84e0d503ac2682db">transferOwnership</a></li><li><a href="https://etherscan.io/tx/0x196d621671c7015868e55edfd7ee4441b3a1d35880139f534c2ef332eda03c98">revokeRole (vault)</a></li><li><a href="https://etherscan.io/tx/0xe9bb435b43adb565ac221c22f21ebf4edb4f67dea3e99104fb3a56f9e775b26f">revokeRole (delegator)</a></li></ul></td></tr><tr><td>2026-07-30</td><td>USDC RWA Liquidity</td><td>Whitelisted seven tokenised RWAs on the Liquid Lane adapter: JAAA, JTRSY, deJAAA, deJTRSY, mF-ONE, mGLOBAL, HYBOND. All acquisition limits and minimum discounts set to 0.</td><td>Whitelisting ahead of enablement, so limits can be set per asset as each Risk Framework assessment closes. At 0 limits no RWA can be acquired.</td><td><a href="https://etherscan.io/tx/0x1f15c266a89fcd4bd7d2147a61a9f6c1683f8efc7f79f6f6ecf192f214b8a135">addTokenToRedeem</a></td></tr><tr><td>2026-07-29</td><td>USDC RWA Liquidity</td><td>Set adapter relative caps (USDC Yield 90%, USDC Prime 10%, Aave v3 0.01%, Liquid Lane 0%) and the deposit queue: USDC Prime, then USDC Yield, then Aave v3. Liquid Lane sits outside the queue.</td><td>Caps plus queue order set the allocation: the most liquid venue fills first, overflow goes to the higher-yielding vault, and caps are maxima rather than targets.</td><td><ul><li><a href="https://etherscan.io/tx/0x548f1b0f5ee2500d9bb6ad1d925a50aa1bfe1a45e96bbbed513b622ec79c8932">setLimits</a></li><li><a href="https://etherscan.io/tx/0xcdd10968691b15617c1211276264a61c71cf4099f6def75454a4dad7a36f38e8">set deposit queue</a></li><li><a href="https://etherscan.io/tx/0x16ebc38707839a0602663973b88dbb36e18cdcc548c13b584ebf17c52639b84e">first auto-allocate</a></li></ul></td></tr><tr><td>2026-07-27</td><td>USDC RWA Liquidity</td><td>Assigned roles: Security Council as admin on the vault and delegator; Curator Safe (<a href="https://etherscan.io/address/0x73af5fcdF830035401e00c322D657982b4a71288"><code>0x73af...1288</code></a>, 2 of 5) for allocate, deallocate, force-deallocate, swap adapters, set adapter limits, set per-asset acquisition terms, pause and unpause the Liquid Lane adapter, set auto-allocate route. Scoped <code>symbiotic_rebalancer_agent</code> to the rebalancing agent.</td><td>Splits authority by response speed: adapter additions, roles, and fees sit with the Security Council; operational parameters, including adapter limits and per-asset terms, with the 2/5 Curator Safe.</td><td><a href="https://curator.symbiotic.fi/vaults/0x8BcD746976885b5832bAD07B4921E3f2dD1D3703/roles">Live roles view</a></td></tr><tr><td>2026-07-27</td><td>USDC RWA Liquidity</td><td>Deployed the vault (<a href="https://etherscan.io/address/0x8BcD746976885b5832bAD07B4921E3f2dD1D3703"><code>0x8BcD...3703</code></a>) with four adapters: Morpho Vault V2 into USDC Yield and USDC Prime, Aave v3 USDC, and Liquid Lane. Performance fee 10%, management fee 0%.</td><td>First KPK-curated Symbiotic vault, providing standby liquidity for tokenised RWA holders exiting before issuer settlement. Onchain name and symbol are immutable; the product name is carried in Symbiotic's vault metadata.</td><td><a href="https://etherscan.io/tx/0x9476481a04a41a7fe8f87c77659a1d70990a8ebd807aaf5411b0cccfc51119e1">Deployment</a></td></tr></tbody></table>


# Automation

At scale, continuous, real-time curation cannot rely solely on manual intervention. KPK uses deterministic agents to support curation. These agents operate within tightly defined permissions and do not exercise discretion. Their role is to execute predefined actions when specific conditions are met. Automation is deliberately limited. Agents can act quickly, but only within clearly defined bounds. They cannot introduce new strategies, expand exposure beyond approved limits, or bypass risk constraints.

### Process

Every KPK agent follows the same shape: it monitors onchain conditions, is triggered by a [Hypernative](https://www.hypernative.io/) alert, and autonomously executes a transaction within predefined bounds, as per our [permissions policy](https://docs.kpk.io/vaults/infrastructure/automation#policies). What that action is depends on the agent.

The rebalance agent, for example, runs the most involved loop:

1. It continuously monitors onchain conditions and is triggered by a Hypernative alert
2. It fetches all relevant data for all markets
3. It estimates projected market APYs or market utilisations for all feasible allocations
4. It calculates the optimal new allocation across markets within predefined risk boundaries that maximises vault APY or minimises exposure
5. If there's a sufficiently high marginal increase, or a decrease in risk (according to a predefined parameter), it builds the transactions and executes them onchain

The exit and replenishment agents follow the same monitor-and-act shape but skip the optimisation steps: the exit agent drains a market on a risk alert, and the replenishment agent tops up agent gas balances.

<figure><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F8KpGNVJq1vFiI59bvRre%2Fimage.png?alt=media&amp;token=856aa8e6-9e46-4892-bd80-b0d3094bf23e" alt="KPK rebalancing agent loop: monitor onchain conditions, fetch market data, compute optimal allocation within risk bounds, build and execute the transaction onchain"><figcaption></figcaption></figure>

Each agent operates through an externally owned account (EOA) that is unique per vault and whitelisted in the Curator Safe's Roles Modifier for a narrow set of functions, targets, and parameter values. Every transaction an agent builds is checked against that allow-list onchain before the Safe will act on it, and reverts if it falls outside. The private keys are generated and stored in a dedicated ETH signer vault, with no human access to the keys. See [Signer architecture](/vaults/infrastructure/signer-architecture) for the full permission model. To ensure these EOAs always have sufficient gas to cover transactions, a script monitors their ETH balance and automatically tops them up.

### Policies

Policies define the set of onchain permissions that the Curator Safe delegates to agents. Because the Curator role itself operates under strict permissions and timelocks at the vault level, the permissions it can delegate to agents are narrow in scope — ensuring that no single agent represents a critical point of failure.

In the event an agent is compromised or acts outside its intended behavior, the blast radius is contained by design. For example, a Shutdown Agent can only set market caps to zero. Should such an agent be compromised, the Curator can promptly restore the caps and revoke the agent's permissions — resulting in funds remaining idle for only a brief window before normal operations resume.

For a detailed breakdown of how permissions are configured, refer to the [permissions article](https://kpk.io/blog/permissions-layer).

### Agents

Vault agents monitor borrow utilisation, APY shifts, price divergence relative to reference venues, oracle liveness, and liquidity depth to keep allocations within risk limits and support competitive yields.

<table><thead><tr><th width="148.4375">Agent</th><th width="258.4453125">Trigger</th><th>Action</th></tr></thead><tbody><tr><td><strong>Rebalance</strong></td><td>APY shifts, deposits, withdrawals, or utilisation changes</td><td>Reallocates across approved markets to improve yield, within tier and cap limits</td></tr><tr><td><strong>Exit</strong></td><td>Risk alerts (oracle staleness, liquidity stress, a collateral pause, price divergence)</td><td>De-risks a market: soft shutdown (drains, caps untouched, reversible) or hard shutdown (caps to 0, drains, permanent)</td></tr><tr><td><strong>Replenish</strong></td><td>An agent EOA's ETH balance falls below its minimum</td><td>Tops the EOA back up so it can keep paying gas</td></tr><tr><td><strong>Monitoring</strong></td><td>Abnormal <code>forceDeallocate</code> activity detected (griefing)</td><td>Sets <code>forceDeallocateFee</code> to 0.5% (default is 0.01%) to deter further abuse, and triggers an immediate rebalance to restore allocations</td></tr></tbody></table>

#### Rebalance agent

The **rebalancing agent** improves capital efficiency in normal conditions, allocating and rebalancing across approved markets using tier- and cap-aware rules, subject to safety checks. It has only the permission to call the reallocate function on the vault contract.

<details>

<summary><strong>Example: rebalance on deposit (USDC Yield, Arbitrum)</strong></summary>

**Trigger event**: On [3 February 2026 at 09:42:56 UTC](https://arbiscan.io/tx/0x05484ef801ffb22dc441ac0a278f283de5b0f3a24a07ed8c9ba07a17fe2ffe70), a large deposit is made into the KPK USDC Yield vault on Arbitrum.

**Process**: The agent checks the vault's total assets and runs an algorithm to determine the optimal allocation for each market, based on the parameters the curation team sets per market, for example, a maximum allocation per market (from the risk team's due diligence) and a minimum withdrawable fraction per market (which ensures liquidity).

**Output**: On [3 February 2026 at 09:43:25 UTC](https://arbiscan.io/tx/0xe99cb5ee8e176d3eb534a911e67e09a0797bf6bff53a56c841488a063c736ee1), the agent rebalanced the vault's allocations after identifying a higher yield, built the transaction, and executed it with the ETH signer. The curation team is notified that a rebalance has taken place.

</details>

#### Exit agent

The **exit agent** is triggered by risk alerts such as oracle staleness, liquidity stress, a collateral pause, or severe price divergence. It reduces or disables a market's exposure, drains the liquidity into the vault's idle buffer, and prioritises safe exits. Exit agents are a family of responses graded to the severity and reversibility of the alert.

{% columns %}
{% column %}
**Soft shutdown**

applies to softer signals that may prove to be false positives, such as a precautionary pause on a collateral asset. The agent stops the vault allocating to the affected market and drains its current exposure, while leaving the market's onchain caps untouched. Because the caps are never changed, re-enabling the market does not require a time-locked governance transaction: once the team confirms the signal was benign, the curation team restores the market, and the vault resumes allocating within minutes. Only the affected market is de-risked, so the rest of the vault continues to earn.
{% endcolumn %}

{% column %}
**Hard shutdown**

applies to confirmed or terminal risk, and to the permanent offboarding of a market. The agent zeroes the market's onchain caps so the vault can no longer allocate to it, then drains the position out.

Setting a cap to zero is not timelocked, so the exit is immediate; re-enabling the market is a governance-class change that runs through the Curator Safe timelock and takes 72 hours.
{% endcolumn %}
{% endcolumns %}

In both cases, the agent only reduces exposure. Exit agents are most developed on KPK's Morpho Vaults and also run on KPK's Euler Vaults.

<details>

<summary><strong>Example: soft shutdown (USDC Yield, Cap pause)</strong></summary>

**Trigger event**: On 9 June 2026 at 22:23 UTC, Cap [paused its cUSD stablecoin](https://etherscan.io/tx/0x9d7eee1581865a470bc9d6aecd877bea0440afcfe81cb79149dcb26dad222945). A Hypernative alert and a PagerDuty page reached the risk curation team within seconds.

**Process**: A soft shutdown was triggered across the markets with exposure to Cap (stcUSD, DUSD, and srRoyUSDC). The exit agent stopped the vault allocating to those markets and drained the available liquidity back to idle, with the [first deallocation](https://etherscan.io/tx/0x5c15f1ea3873bd1d74f74fb1251568a701edee4b5fee56dd401b4c3ebfb0c828) executing around 15 seconds after the alert. Where a market's liquidity was partly committed to borrowers, the agent withdrew what was available, and the rest followed as borrowers repaid. The onchain caps were left untouched throughout.

**Output**: Around 5 minutes after the pause, Cap confirmed it was a false positive triggered by MEV activity, and unpaused roughly 20 minutes after the original pause. Because no onchain caps had been changed, the markets were re-enabled after human review, and the vault resumed allocating without a curator unpause.

</details>

<details>

<summary><strong>Example: hard shutdown (ETH Prime, market delisting)</strong></summary>

**Trigger event**: On 10 February 2026, the curation team wanted to delist a collateral market on the ETH Prime vault on mainnet.

**Process**: The vault held 13.03 WETH in the market, against \~22 WETH in available liquidity, so the full position could be withdrawn in a single move. The exit agent set the market's supply cap to 0, which stops any new allocation, then withdrew the entire 13.03 WETH position to idle.

**Output**: On [10 February 2026 at 10:18:35 UTC](https://etherscan.io/tx/0x79981b9fc9a003414f27ab36d8adf22f95449adc5c8a692fb9ad2933598d9789), the agent successfully executed. The supply cap was reduced from 1b to 0. The curation team has been notified that an exit action has occurred.

</details>

#### Replenish agent

The **replenishment agent** ensures that every agent EOA always has enough ETH to operate. It monitors a list of registered accounts and tops them up whenever a balance drops below its threshold:

* Rebalance agents require a minimum of 0.01 ETH and are topped up to 0.03 ETH
* Exit agents require a minimum of 0.05 ETH and are topped up to 0.05 ETH

<details>

<summary><strong>Example: gas top-up (USDC Yield rebalancing agent)</strong></summary>

**Trigger event**: On 4 May 2026, the agent ran its routine check of the ETH balance on every agent EOA.

**Process**: The [USDC Yield rebalancing agent](https://etherscan.io/address/0x7a35f5dcd3faa4bd617b7d7c3cc9b9ea6353291f) had fallen below its 0.01 ETH minimum, so it was queued for a top-up to the 0.03 ETH target.

**Output**: On [4 May 2026 at 16:00:47 UTC](https://etherscan.io/tx/0xabee740b3cfb4b6ae13d9c98ea08f4a92184b61904f40a51c17ab7b4be8c2dab), the agent sent 0.03 ETH to the USDC Yield rebalancing agent, restoring it to the operating balance.

</details>

#### Monitoring agent

The **monitoring agent** detects griefing attacks against the vault and automatically sets the `forceDeallocateFee` to 0.5% (the default is 0.01%) to deter repeated abuse of the `forceDeallocate` function. It also triggers an immediate rebalance, so allocations disrupted by the attack are restored without waiting for the next scheduled cycle.

<details>

<summary><strong>Example: forceDeallocate griefing campaign (Jun 2026)</strong></summary>

**Trigger event**: On 21 June 2026, an external actor ran an ecosystem-wide `forceDeallocate` griefing campaign — 58 calls across 26 Morpho V2 vaults. KPK vaults were a subset of the targets. Each call temporarily routed allocated liquidity back to idle, disrupting active allocations until the next rebalancing cycle (\~15 minutes).

**Process**: The monitoring agent detected the abnormal volume of `forceDeallocate` calls onchain and identified the attack pattern.

**Output**: The agent [raised the `forceDeallocateFee` from 0% to 0.5%](https://etherscan.io/tx/0x6601b8ffa2a8b6b302f45f3f30e999fcdc3920579e041e4990c0d1b46579ef4b#eventlog) on the USDC Yield vault, making further griefing economically prohibitive. The curation team was notified of the intervention.

</details>


# Coverage

[KPK's curated vaults](https://opencover.com/vaults/kpk) offer discretionary cover so that depositors can hold a covered position that responds to certain technical and economic loss events. Cover is an optional, vault-level feature: it does not change how a vault is curated, allocated, or monitored, and it sits alongside the controls described in the Risk framework.

The cover is provided through a partnership between [Nexus Mutual](https://nexusmutual.io/) and [OpenCover](https://opencover.com/). KPK arranges and maintains the cover for its vaults. Three parties are involved, with distinct roles.

<table><thead><tr><th width="160" valign="top">Party</th><th>Role</th></tr></thead><tbody><tr><td valign="top"><strong>Nexus Mutual</strong></td><td>Underwriter and risk carrier. Provides the discretionary mutual cover that stands behind the covered position, and assesses and decides claims under its cover terms.</td></tr><tr><td valign="top"><strong>OpenCover</strong></td><td>Distributor and broker. Builds and operates the Covered Vault contracts through which the cover is arranged, priced, and maintained. OpenCover distributes Nexus Mutual cover; it is not the risk carrier.</td></tr><tr><td valign="top"><strong>KPK</strong></td><td>Curator. Has arranged cover for its vaults. KPK is not the provider, does not bear the risk, and does not guarantee any payout.</td></tr></tbody></table>

The Covered Vault contracts are built on tokenised-vault standards and have completed independent security audits (Nethermind and Sherlock). For the contract-level detail, see the [OpenCover documentation](https://opencover.com/).

### How the cover works

OpenCover's Covered Vaults wrap a vault position with cover at the contract level, so a depositor activates cover by depositing rather than by arranging cover separately.

{% stepper %}
{% step %}
**Covered Vault wrapper**

A depositor enters through an OpenCover Covered Vault, a contract that wraps the underlying KPK-curated vault. The depositor keeps the same underlying exposure, strategy, and yield as the uncovered vault.
{% endstep %}

{% step %}
**Cover activation**

Cover is activated by depositing into the Covered Vault, without leaving the vault interface or arranging cover separately. Exiting the Covered Vault ends the cover on that position.
{% endstep %}

{% step %}
**Premium streaming**

The premium streams continuously from the position's yield, with automatic renewal and no minimum term. The depositor pays only for the duration of the cover held.
{% endstep %}
{% endstepper %}

For Covered Vaults, OpenCover administers the cover programmatically as the Covered Vault Manager, so depositors are not required to file claims individually. Claims are reviewed by Nexus Mutual's Claims Committee following a cool-down period.

### Scope

Cover responds to specific technical and economic loss events affecting the covered position, as defined in the Protocol Cover terms. The cover responds to a loss of funds caused by any of the following, as defined in the cover wording:

<table><thead><tr><th width="200" valign="top">Event</th><th width="544.52734375">Description</th></tr></thead><tbody><tr><td valign="top"><strong>Smart contract exploits</strong></td><td>A code bug or error that causes the designated protocol to be used in an unintended way.</td></tr><tr><td valign="top"><strong>Oracle failure</strong></td><td>Incorrect price-feed data used by the protocol's contracts, where the error exceeds 1% for stablecoin-related oracles or 2.5% for other assets, arising from faulty configuration, missing safeguards against unauthorised updates, or feeds that fail to update.</td></tr><tr><td valign="top"><strong>Oracle manipulation</strong></td><td>Price-feed data that is deliberately corrupted and leads to a loss of funds.</td></tr><tr><td valign="top"><strong>Liquidation failure</strong></td><td>Keepers cannot liquidate collateral backing unhealthy borrow positions, resulting in bad debt socialised across lenders in the affected market, or they liquidate that collateral for less than 80% of fair realisable market value.</td></tr><tr><td valign="top"><strong>Governance takeovers</strong></td><td>A malicious actor forces through a malicious upgrade to a designated protocol contract.</td></tr></tbody></table>

Covered Vaults do not cover all losses, and they do not eliminate risk. Market movements, yield variation, and other events outside the cover wording are not covered, and the cover is subject to exclusions and a deductible.


# Risk framework

Risk curation is central to every KPK product. This page explains how assets and strategies are **evaluated, classified and monitored** so that each curated product rests on clear, data-driven risk assumptions.

The framework combines structured due diligence, independent risk signals, allocation rules and real-time monitoring, and is applied to every asset or strategy considered for use as collateral or another core component within a KPK-curated product.

## Initial due diligence

Each asset, market, strategy or counterparty undergoes a structured review under our Due Diligence Framework, covering **more than 150 checks across 12 risk dimensions**: smart contract, upgradeability, governance, oracle, counterparty, market, liquidity, leverage and reflexivity, collateral, balance sheet, operational, and monitoring.

{% stepper %}
{% step %}
**Onchain Assessment**

The onchain review examines the design and behaviour of all relevant contracts and dependencies, including:

* Smart contract logic, upgradeability and access controls.
* Oracle mechanisms, staleness checks and manipulation vectors.
* External dependencies such as bridges or third-party contracts.
* Liquidity depth, peg mechanisms and historical behaviour.
  {% endstep %}

{% step %}
**Offchain Validation**

Complementing the onchain review, KPK conducts targeted offchain checks to identify governance, operational and legal risks, including:

* Governance structure and upgrade powers.
* Admin key security, timelocks and emergency procedures.
* Entity structure and operational resilience of the issuer or core contributors.
* Track record, historical incidents and response capabilities.
* External audits and relevant disclosures.
  {% endstep %}

{% step %}
**External Risk Signals**

KPK integrates **independent third-party risk assessments** into its curation process to complement internal reviews. Platforms such as Credora and Exponential provide additional scores on protocol health, collateral behaviour and systemic exposure. For stablecoin and wrapped-fiat collateral, Pharos Watch adds peg history, reserve composition, and executable DEX liquidity depth measured after concentration and stress discounts, which feeds directly into liquidity-based tiering and LLTV decisions.

These signals do not replace internal due diligence but act as a supplementary layer of risk intelligence, especially during the initial assessment and ongoing monitoring.
{% endstep %}

{% step %}
**Scored Outcome**

Each dimension receives a severity score from 1 (minimal) to 5 (critical) with an explicit confidence level. The overall rating is the worst dimension score, not an average, so a single critical finding sets the profile.

Scores are deliberately biased towards caution. A risk is judged by the harm it would cause rather than by an estimate of how likely it is, because probability is rarely knowable in DeFi and severity usually is. Where evidence is thin, the resulting low confidence counts against an asset rather than for it. Mitigants such as timelocks and audits are recorded as context but not netted off the score, so a well-managed risk still shows up as a real exposure.

Findings are traced to primary sources, and every assessment is independently reviewed before it informs an allocation decision. Conclusions are one of four: proceed, proceed with conditions (e.g. a lower cap or tighter LLTV), defer pending specific open questions, or decline.
{% endstep %}
{% endstepper %}

## Tiering and allocation

Once an asset or strategy clears due diligence, it is assigned a **risk tier**. Tiers are assigned per market rather than per asset in the abstract: the same collateral can sit in different tiers depending on the loan asset, LLTV, oracle route and liquidation venue it is paired with. Each tier corresponds to predefined **allocation rules,** which limit the product's exposure to that market.

Risk tiering reflects factors such as liquidity depth, oracle design, protocol maturity, and dependency stack complexity. It is applied during **product creation** (e.g. for Gearbox Earn Pools) or **strategy selection** (e.g. Morpho or Euler vaults) to enforce diversification and risk-weighted exposure.

<table><thead><tr><th width="81.51171875">Tier</th><th>Typical Characteristics</th><th>Examples</th></tr></thead><tbody><tr><td>A</td><td>Deep exit capacity, either as onchain liquidity (≥$50m, with ≥$10m executable at 2% slippage) or a primary redemption path with defined settlement mechanics and a transparent reserve; highly reliable oracles (e.g. Chainlink or RedStone feed with ≤1% deviation tolerance and ≤24h heartbeat); mature protocols (≥12 months operating history); clean recent operational track record</td><td>High-liquidity LSTs with native redemption (e.g. wstETH), major wrapped BTC (e.g. WBTC, cbBTC), fully-backed fiat stablecoins (e.g. RLUSD)</td></tr><tr><td>B</td><td>Moderate liquidity, well-known protocols with some newer components, diversified oracle setup</td><td>LRTs from established teams (e.g. weETH), LSTs with moderate liquidity (e.g. cbETH, rETH)</td></tr><tr><td>C</td><td>Lower liquidity or less battle-tested oracles, emerging protocols, moderate dependency complexity</td><td>Newer LRTs, emerging yield-bearing stablecoins including those relying on issuer-run oracles (e.g. savUSD, sNUSD), or assets using alternative oracle setups (e.g. API3, RedStone Pull Oracles)</td></tr><tr><td>D</td><td>Experimental or early-stage assets, limited liquidity, higher dependency or governance risk</td><td>Niche or newly launched assets and strategies (e.g. srRoyUSDC)</td></tr></tbody></table>

> *Risk tiers are indicative and may vary by product. Actual allocation limits are defined per product and tracked through change logs.*

## Monitoring and response

Risk doesn’t end once an asset is listed. KPK continuously monitors both **onchain and offchain signals** to track evolving conditions and take action when needed.

Monitoring is designed as part of the assessment, not added afterwards. Each review ends in a monitoring plan derived from the risks it found: the specific events, admin calls and state thresholds that would signal a risk is materialising, each with a severity and a defined response. That plan is deployed directly into KPK's alerting stack, so the risks identified on paper are the risks watched in production.

Observability is itself a scored dimension. Where a protocol's critical state transitions cannot be detected onchain, because contracts are unverified or material actions emit no events, that raises its risk score and can be enough to keep it out of a KPK product.

<figure><picture><source srcset="/files/34MAt2N60DiVR810qarh" media="(prefers-color-scheme: dark)"><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F4f5jnC8tNlHRfpPIEQci%2FKPK%20MONITORING%20SYSTEMS_L.png?alt=media&amp;token=d9d24e9a-26a7-49a5-baa7-3032be08e96b" alt=""></picture><figcaption></figcaption></figure>

Different approaches to monitoring are adopted for the different components of each curated product.

* **Collateral monitoring** covers the assets used as security against KPK's lending markets and the issuers and protocols that stand behind them, including their contracts, governance, oracles, and offchain operating reality. KPK tracks governance proposals and contract upgrades that may affect collateral safety, and watches for pause events on collateral tokens that could break redemption from the underlying. On the oracle side, KPK monitors staleness, compares oracle prices to reference venues to detect anomalies, and watches for ownership-transfer events on the price feeds themselves. Issuer- and protocol-level signals are also tracked, including legal-structure changes, multisignature activity, shifts in underlying strategy, and staking performance such as slashing events. Liquidity-depth drops on major DEX and CEX venues feed into the same monitoring loop. Exploit-detection coverage extends to contracts immediately adjacent to the collateral, such as redemption layers and custodial wrappers.
* **Protocol monitoring** covers the lending platform and its dependencies as code, including the contracts and governance KPK does not control. Governance proposals and timelock changes are scrutinised to catch hostile or rushed upgrades. Core contract upgrades, where the protocol exposes them, are tracked. Vault-infrastructure governance is also tracked, including multisigs that KPK participates in (e.g. the Gearbox Instance Owner multisig). Changes to admin keys or key control structures at the protocol level trigger alerts. Exploit-detection coverage extends to governance modules adjacent to the venue.
* **Strategy monitoring** covers the live state of KPK's strategy running on the platform, including utilisation, borrower behaviour, market depth, and KPK's own curator controls. KPK watches liquidation activity and borrower position health, market-level liquidity changes (a significant TVL drop in a market can prompt offboarding), curator concentration, and available withdrawal liquidity and borrow activity. For example, utilisation levels above 97% are flagged as potential withdrawal risk. KPK's own curator and agent permissions are strictly scoped through the Permissions Layer.

The diagram below illustrates how different risk categories map to concrete monitoring actions:

<picture><source srcset="/files/p5BnlEMRePoq469n2FLT" media="(prefers-color-scheme: dark)"><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FZxLDBRpiPgOVtltEG4Bm%2FRisk_Framework_L.png?alt=media&amp;token=1ad9ee0c-92ea-4c68-b6b3-d425552abd57" alt=""></picture>

Across all three categories, signals come from a mix of onchain monitoring, third-party risk providers, and offchain sources, including posts from security researchers and incident-response accounts on X triaged by the team.

**Response mechanisms** combine automated and manual layers. Risk alerts from providers like Hypernative and Cyvers, peg and liquidity alerts from Pharos Watch, as well as internal monitors, flag anomalies in real time. Some signals trigger automated protective actions, typically executed within 1–2 Ethereum blocks (under 30 seconds) and scoped to the affected market or position so unaffected exposure continues to earn; other signals are continuously monitored and routed to human review when the appropriate response benefits from context that a deterministic agent cannot capture. The exact response architecture varies by venue and is documented in [Automation](/vaults/infrastructure/automation) and the individual protocol sections.

Protective measures such as pausing markets or tightening collateral parameters can be triggered automatically when monitored signals warrant, or executed by the Emergency Admin role when a human review takes the action. More complex situations, including multi-asset or governance-related incidents, are handled through structured manual reviews. Risk tiers or parameters are updated accordingly when material changes occur, and every action is recorded for full transparency.

KPK's automated responses are graded to the severity and reversibility of the signal. A softer signal that may prove to be a false positive, for example a precautionary pause on a collateral asset, triggers a reversible soft shutdown that de-risks only the affected market and is unwound after human review once the team confirms the signal was benign, with no onchain cap change needed. Confirmed or terminal risk, and permanent offboarding, trigger a hard shutdown that zeroes the market's onchain caps and drains the position; re-enabling a market after a hard shutdown is a governance-class change that runs through the Curator Safe timelock and takes 3 days. Designing the soft path to be reversible lets KPK act on early signals while keeping the rest of the vault online.


# Signer architecture

Every KPK-curated vault is operated through a dedicated set of Safes and onchain permissions. The architecture isolates each vault, scopes automation to a single role, and keeps human signers behind multi-signature thresholds and timelocks. No individual signer, automated or human, can unilaterally move funds or change a vault's risk parameters.

A [Safe](https://safe.global/) is a smart-contract multi-signature wallet on Ethereum and EVM chains: transactions require approval from a configurable number of signers (e.g. 2-of-5) before execution, with the signing rules enforced onchain rather than at a custodian.

### Per-vault Curator Safe

Each KPK-curated vault, whether on Morpho, Euler, Gearbox, or Symbiotic, has its **own Curator Safe**. The Safe governs day-to-day curation actions for that vault and nothing else.

Curator Safes share the same signer set across vaults, which always includes the **KPK Risk Council Safe** (5-of-8). The Risk Council Safe also owns the vault contract itself, giving it authority over higher-impact actions such as adding or removing markets.

Each Curator Safe carries a **distinct** [**Zodiac Roles Modifier**](https://docs.roles.gnosisguild.org/) **configuration**, the onchain instance of KPK's [Permissions Layer](https://kpk.io/blog/permissions-layer). No role configuration is shared across vaults.

### Onchain permission enforcement

The Roles Modifier is a contract in the transaction path, not an offchain policy file. Every call an agent makes is routed through it and checked against that role's allow-list before the Safe will act on it. A call that is not permitted reverts onchain.

The allow-list is scoped at three levels:

* **Target.** Which contracts the role may call, for example the vault and its adapter, and nothing else.
* **Function.** Which functions on those contracts, for example `reallocate` but not `setCurator`.
* **Parameter.** Which argument values are acceptable, for example only the markets already approved for that vault, and only that vault's own adapter as the destination.

Parameter-level scoping is what makes the permission narrow rather than merely role-based. A protocol-level Allocator role permits reallocation across every market a vault has enabled. The Roles Modifier restricts a given agent to a defined subset of those markets, within defined bounds.

Two properties follow from this:

* **The security boundary is the permission set, not the agent code.** A bug in an agent can at worst perform a permitted action at the wrong moment. It cannot perform an action the role does not allow, regardless of what the code attempts.
* **Giving an agent a new capability is an onchain change.** Extending what an agent may do requires updating the role, which is a governance-class transaction under timelock (see [Defence in depth](#defence-in-depth)). Deploying new agent code changes nothing about what is permitted. If code and role disagree, the transaction reverts.

No agent holds funds. Depositor assets sit in the vault contract, and the Curator Safe holds the roles over it. An agent EOA holds only a key that can propose a call through the Roles Modifier, and that key lives in a dedicated signer service that signs and nothing else: it does not choose what to send, and it does not broadcast.

### Automation addresses are vault-specific

KPK agents (rebalancing, exit, and replenishment, see [Automation](/vaults/infrastructure/automation)) operate through externally owned accounts (EOAs) that are **unique per vault**. Each EOA is whitelisted in the Curator Safe's Roles Modifier for a narrow set of functions, typically reallocation between approved markets or reducing a market's cap to zero.

Key properties of these automation EOAs:

* **One EOA per vault per agent type.** A rebalancing EOA for the USDC Yield vault cannot act on the ETH Prime vault, and vice versa.
* **No human key access.** Private keys are generated and held in a dedicated ETH signer vault. No team member can sign manually from these addresses.
* **Single-purpose.** The addresses are used exclusively for their assigned agent function. They do not hold other assets and do not interact with other contracts.
* **Role-scoped permissions.** At most, an automation EOA holds an Allocator-equivalent role. It cannot add markets, change caps upward, or change vault ownership.

To date, KPK automation has executed over 8,800 rebalances and moved more than $6bn across [KPK-curated Morpho vaults](https://dune.com/kpk/kpk-morpho-vaults).

### Manual signers

Beyond automation, the remaining signers on Curator Safes are **KPK team members**. The same individuals sign across all KPK-curated vaults from labelled, curation-only addresses, for example [`kpk Deployer 1`](https://etherscan.io/address/0xF0Cf1e3Ec6264b03241826f5b2aBda17B4352A75) and [`kpk Deployer 2`](https://etherscan.io/address/0x331D9C769185AE25233314859d53c2b9203a1204) on Etherscan.

These addresses are used strictly for activities required to operate KPK-curated products. This includes signing Curator Safe transactions and the onchain steps that support curation, such as token transfers, approvals, and DEX swaps performed in the course of market creation or vault setup. No activity outside the scope of curating KPK products takes place from these addresses.

Standard operational practices apply to every Safe transaction:

* Calldata is decoded and inspected before signing.
* Transaction hashes are independently verified on hardware wallets.
* Transactions are simulated before signing.

### Defence in depth

The architecture is designed so that no single point of failure, automated or human, can produce a material change to a vault.

* **Threshold signing.** Every Curator Safe transaction requires at least two signatures. A single compromised signer cannot execute a transaction alone.
* **Timelocks on governance-class changes.** Changes that can expand permissions or increase risk, for example role updates, enabling a new market, raising allocation ceilings, or adding an adapter, execute under protocol-specific timelocks (e.g. Morpho v2's 3/7/14-day schedule, Euler Earn's 3-day delay, Gearbox's 24-hour delay on pool changes). Symbiotic V2 ships no built-in timelock on its economic parameters, so there the separation is by authority rather than by delay: parameters that can widen exposure sit with the Security Council, operational parameters with the Curator Safe. Operational actions within pre-approved bounds, such as reallocation between enabled markets or setting a market cap to zero, are not timelocked so that agents can respond within seconds. The **Guardian** role can intervene during the timelock window if needed. Coverage varies by protocol and is documented on each vault page.
* **Bounded automation.** The worst-case outcome of an automation key compromise is reallocation between pre-approved markets, within existing caps. Funds cannot be withdrawn to an external address or moved to a non-approved market. This bound is enforced by the Roles Modifier at execution time, not by the agent's own logic, so it holds even against a fully compromised signer.
* **Monitoring.** Hypernative and internal monitors flag abnormal transactions in real time, including queued timelocked actions.

For the broader monitoring and response framework, see [Risk framework](/vaults/infrastructure/risk-framework).


# Overview

This section is for **integrators** building on top of KPK vaults: wallets adding an Earn section, fintech apps offering yield to end-users, custodians and CEXs allocating client assets, and distribution partners that want to charge a fee on top of KPK strategies.

End-users who simply want to deposit should use the protocol UI directly. Vault addresses, supported assets, and current parameters are listed on each [vault page](https://github.com/karpatkey/kpk-docs-gitbook/tree/main/vaults/vaults/README.md).

This page covers the protocol-agnostic shape of an integration: who is integrating, what they need to build, and which functions to wire up. For protocol-specific nuances (SDKs, batch flows, referral codes, fee wrappers), follow the links into the per-protocol pages:

<table data-view="cards"><thead><tr><th align="center"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td align="center"><strong>Morpho</strong></td><td><a href="/vaults/integration/integration/morpho">Morpho</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FMIqcrNNtLDVRiUFy3e5G%2FMorpho_L.png?alt=media&amp;token=58341344-6625-4972-a864-36a3a1ec205a">Morpho_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FdqdWFE1qo3TqtO2THZ7S%2FMorpho_D.png?alt=media&amp;token=f1e52786-841a-4bae-926b-5b72490a5c8f">Morpho_D.png</a></td></tr><tr><td align="center"><strong>Euler</strong></td><td><a href="/vaults/integration/integration/euler">Euler</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FGzVGquHQLEKJOzPpDLDb%2FEuler_L.png?alt=media&amp;token=12a5e78c-6326-4039-ad24-73e3b0a68ef1">Euler_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FItH2nBKGVFLoL2lWppcs%2FEuler_D.png?alt=media&amp;token=022b29bb-f3e9-47aa-80b3-692565bd070c">Euler_D.png</a></td></tr><tr><td align="center"><strong>Gearbox</strong></td><td><a href="/vaults/integration/integration/gearbox">Gearbox</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FLp2dxo1JINOQgCRlw1xC%2FGearbox_L.png?alt=media&amp;token=8514dac5-07e5-4c63-9e68-fd48f2d5879e">Gearbox_L.png</a></td><td><a href="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FXtwpOvJ0ry0bQUVnx5pX%2FGearbox_D.png?alt=media&amp;token=2d158910-cb0a-4ee4-ae07-1dcb93c30621">Gearbox_D.png</a></td></tr></tbody></table>

### Pick your integration path

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Wallets and fintech apps</strong></td><td>Adding an Earn section to a wallet, neobank, or savings app where users hold their own keys.</td><td><a href="#building-an-earn-section">#building-an-earn-section</a></td></tr><tr><td><strong>Existing dApps and aggregators</strong></td><td>Already have an Earn product; just need vault references and the right SDK or interface.</td><td><a href="#sdks-and-direct-integration">#sdks-and-direct-integration</a></td></tr><tr><td><strong>Custodians and CEXs</strong></td><td>Allocating from a custodial wallet via direct contract calls.</td><td><a href="#direct-contract-integration">#direct-contract-integration</a></td></tr><tr><td><strong>Distribution partners</strong></td><td>Want to charge a performance or management fee on top of a KPK vault.</td><td><a href="#fee-wrappers-for-distribution-partners">#fee-wrappers-for-distribution-partners</a></td></tr></tbody></table>

### Building an Earn section

If you don't yet have an Earn surface, this section walks through what to build and how the pieces connect. If you already have one, skip to [SDKs and direct integration](#sdks-and-direct-integration).

An Earn section gives users a way to put idle assets to work. They deposit from their own wallet, yield accrues over time, and they can withdraw whenever they want. No intermediary holds the funds; every interaction is between the user's wallet and the vault.

#### What to build

A complete Earn integration covers four areas:

* **Position display.** Show the user their current deposit, accrued yield, current APY, and any claimable rewards. Update in real time.
* **Deposit and withdrawal flows.** Deposits require an asset approval (or permit signature) before the deposit transaction; withdrawals are a single call. Give users clear feedback on what's happening at each step.
* **Rewards.** Some vaults distribute additional incentive tokens on top of base yield. Surface claimable amounts and let users claim independently of their deposited position.
* **Monitoring.** Refresh user position and yield data continuously, and surface meaningful changes to vault parameters when they happen.

#### Integration checklist

1. Pick the vault(s) from the [vault directory](https://github.com/karpatkey/kpk-docs-gitbook/tree/main/vaults/vaults/README.md) and copy the addresses from each vault's **Key information** table.
2. Choose the integration path based on which protocol(s) the vault(s) live on:
   * Morpho-hosted vaults: install the [Morpho SDK](/vaults/integration/integration/morpho#integrating-via-the-morpho-sdk).
   * Euler-hosted vaults: use the [EVK batch or direct ERC-4626 flow](/vaults/integration/integration/euler).
   * Gearbox-hosted vaults: use [`depositWithReferral`](/vaults/integration/integration/gearbox#deposit) with the KPK referral code.
3. Display vault APY and the user's position via the relevant SDK or contract reads.
4. Build the deposit flow following the per-protocol page.
5. Build the withdrawal flow following the per-protocol page.
6. Surface claimable rewards via the [Merkl API](https://docs.merkl.xyz/) where applicable.
7. Set up real-time refresh for position and yield data.

### SDKs and direct integration

All KPK vaults are ERC-4626 compliant, so any integrator can interact with them via the standard `deposit`, `withdraw`, `redeem`, `previewDeposit`, and `previewWithdraw` methods. Each protocol exposes additional helpers and routing on top of the ERC-4626 surface:

* **Morpho-hosted vaults** are best integrated through the [Morpho SDK](https://docs.morpho.org/tools/offchain/sdks/morpho-sdk/). The SDK handles approvals, permit signatures, slippage protection, and bundler routing into the underlying v1 markets. See [Morpho integration](/vaults/integration/integration/morpho#integrating-via-the-morpho-sdk) for code samples.
* **Euler-hosted vaults** can be integrated either through Euler's EVK `batch()` flow (recommended when bundling Permit2 + deposit) or via direct ERC-4626 calls on the vault. See [Euler integration](/vaults/integration/integration/euler).
* **Gearbox-hosted vaults** are integrated through direct ERC-4626 calls, with `depositWithReferral` used in place of the standard `deposit` so KPK is correctly attributed. See [Gearbox integration](/vaults/integration/integration/gearbox).

### Direct contract integration

For custodians, CEXs, and onchain protocols that prefer raw contract calls without a JavaScript SDK, all KPK vaults expose the standard ERC-4626 interface: `approve` the vault on the underlying asset, then call `deposit(assets, receiver)` or `withdraw(assets, receiver, owner)` on the vault.

The protocol-specific deposit and withdrawal flows below cover the additional steps each protocol requires (bundlers, batch calls, referral codes):

* [Morpho direct contract integration](/vaults/integration/integration/morpho#direct-contract-integration)
* [Euler direct contract integration](/vaults/integration/integration/euler#deposit)
* [Gearbox direct contract integration](/vaults/integration/integration/gearbox#deposit)

### Fee wrappers for distribution partners

Distribution partners can request KPK to deploy a fee wrapper on top of a curated vault, charging their users a performance or management fee on the underlying KPK strategy without changing the vault itself.

<figure><picture><source srcset="/files/mLlZZOPJEjtduRX2BuxC" media="(prefers-color-scheme: dark)"><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2FJRuddblXhmA69tf1nUEf%2FMORPHOV2_L.png?alt=media&amp;token=61244850-d3c6-412d-8497-cc1bf6f976c5" alt=""></picture><figcaption></figcaption></figure>

Fee wrappers are currently available for **Morpho-hosted vaults** through the [Morpho Fee Wrapper](https://docs.morpho.org/build/earn/concepts/fee-wrapper). See [Morpho integration → Fee wrapper](/vaults/integration/integration/morpho#fee-wrapper-for-distribution-partners) for the full feature set, including flexible fee configuration and optional permissioned setups.

For partners interested in a fee arrangement on a Euler- or Gearbox-hosted vault, contact <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact) to discuss what's possible.

### Protocol-specific integration

For deposit and withdrawal flows, SDK details, and protocol-specific quirks (bundlers, batch calls, referral codes, fee wrappers), follow the relevant protocol page:

* [Morpho integration](/vaults/integration/integration/morpho)
* [Euler integration](/vaults/integration/integration/euler)
* [Gearbox integration](/vaults/integration/integration/gearbox)

### Support

For integration questions, technical reviews, or hands-on help, reach out to <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact).


# Morpho

This page covers the Morpho-specific nuances of integrating KPK vaults: the Morpho SDK, direct contract calls, and the Morpho Fee Wrapper. For the general integrator overview and audience-routing, start at the [Integration page](/vaults/integration/integration).

End-users who simply want to deposit should use the [Morpho UI](https://app.morpho.org/) directly. Vault addresses, supported assets, and current parameters are listed on each [vault page](/vaults/vaults/morpho).

{% hint style="info" %}
**Where deposits end up:** KPK Morpho v2 vaults route deposits through the [**Markets V1 Adapter**](https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2) directly into individual Morpho v1 markets. Integrators tracing the flow of funds should follow the adapter address listed on each vault page (under **Key information**) to see the markets the v2 vault is currently allocated to.
{% endhint %}

### Integrating via the Morpho SDK

The Morpho SDK handles contract interactions, approvals, slippage protection, and transaction construction. It returns ready-to-send transaction objects so the integrator focuses on UX, not low-level encoding.

#### 1. Install and instantiate

```bash
npm install @morpho-org/morpho-sdk
```

```typescript
import { MorphoClient } from "@morpho-org/morpho-sdk";
import { mainnet } from "viem/chains";

const morpho = new MorphoClient(walletClient, {
  supportSignature: true,
  supportDeployless: false,
});

// KPK USDC Prime on Ethereum
const vault = morpho.vaultV2(
  "0x4Ef53d2cAa51C447fdFEEedee8F07FD1962C9ee6",
  mainnet.id,
);
```

All KPK vaults are ERC-4626 compliant and built on Morpho v2. Vault addresses live on each vault page; for example:

| Vault          | Chain    | Address                                      |
| -------------- | -------- | -------------------------------------------- |
| KPK USDC Prime | Ethereum | `0x4Ef53d2cAa51C447fdFEEedee8F07FD1962C9ee6` |
| KPK USDC Yield | Ethereum | `0xD5cCe260E7a755DDf0Fb9cdF06443d593AaeaA13` |
| KPK ETH Prime  | Ethereum | `0xBb50A5341368751024ddf33385BA8cf61fE65FF9` |
| KPK USDC Yield | Arbitrum | `0x5837e4189819637853a357aF36650902347F5e73` |

For the full list, see the [Morpho vault directory](/vaults/vaults/morpho).

#### 2. Read vault and position data

`vault.getData()` returns the onchain state needed to display APY, total assets, and convert between shares and assets.

```typescript
const accrualVault = await vault.getData();

// accrualVault.totalAssets        → total assets in the vault
// accrualVault.toAssets(shares)   → convert user shares to asset amount
// accrualVault.toShares(assets)   → convert asset amount to shares
```

For APY and reward-rate data, query Morpho's offchain API alongside the onchain state. See the [Morpho API reference](https://docs.morpho.org/tools/offchain/) for endpoints.

#### 3. Deposit flow

The SDK uses a two-step pattern: first resolve what's needed (approvals or permit signatures), then build the final transaction.

```typescript
import { parseUnits } from "viem";

const { buildTx, getRequirements } = vault.deposit({
  amount: parseUnits("1000", 6), // 1,000 USDC
  userAddress: USER_ADDRESS,
  accrualVault,
});

const requirements = await getRequirements();
// requirements: (ERC20Approval | Permit | Permit2)[]
// Surface each requirement to the user, collect signatures or send approval txs.

const tx = buildTx(/* signed requirement, if any */);
await walletClient.sendTransaction(tx);
```

Slippage protection is applied automatically: the SDK enforces a `maxSharePrice` ceiling to guard against ERC-4626 inflation attacks.

#### 4. Withdrawal flow

```typescript
// Withdraw a specific asset amount
const { buildTx: buildWithdrawTx } = vault.withdraw({
  amount: parseUnits("500", 6), // 500 USDC
  userAddress: USER_ADDRESS,
});
await walletClient.sendTransaction(buildWithdrawTx());

// Or redeem a specific share amount
const { buildTx: buildRedeemTx } = vault.redeem({
  shares: sharesToRedeem,
  userAddress: USER_ADDRESS,
});
await walletClient.sendTransaction(buildRedeemTx());
```

Withdrawals beyond the vault's idle buffer are routed through the configured liquidity adapter into the underlying Morpho v1 markets. If neither idle nor the liquidity adapter can cover the withdrawal, the SDK can construct a `forceDeallocate` call to pull liquidity from a specific market; see [Liquidity and `forceDeallocate`](/vaults/vaults/morpho#liquidity-and-forcedeallocate) for details.

#### 5. Rewards

Some vaults receive MORPHO incentives or other reward campaigns distributed via Merkl. Query the Merkl API by user address to fetch claimable amounts:

```typescript
const res = await fetch(
  `https://api.merkl.xyz/v3/userRewards?user=${userAddress}`,
);
const rewards = await res.json();
```

The claim is a separate transaction and does not modify the user's deposited position.

### Direct contract integration

For custodians, CEXs, and onchain protocols that prefer raw contract calls without a JavaScript SDK, KPK vaults expose the standard ERC-4626 interface. The flows below use **KPK USDC Prime on Ethereum** (`0x4Ef53d2cAa51C447fdFEEedee8F07FD1962C9ee6`) as the example; substitute the vault address for the asset and chain you need from the [vault directory](/vaults/vaults/morpho).

{% hint style="info" %}
For the canonical Morpho bundler and adapter contract addresses across chains, see the [Morpho contract addresses page](https://docs.morpho.org/get-started/resources/addresses/). Addresses change occasionally; always cross-reference before integrating.
{% endhint %}

#### Deposit

The simplest deposit path is a direct ERC-4626 call:

{% stepper %}
{% step %}
**Approve the vault to spend USDC.**

Call `approve(spender, amount)` on USDC (`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`):

* `spender` = the KPK vault address
* `amount` = the deposit amount, in token units
  {% endstep %}

{% step %}
**Deposit into the vault.**

Call `deposit(assets, receiver)` on the KPK vault:

* `assets` = the deposit amount
* `receiver` = the address that should receive the vault shares (typically the depositor)
  {% endstep %}
  {% endstepper %}

For multi-step flows that batch approval and deposit in a single transaction (e.g. via Permit2), use Morpho's Bundler3. The Bundler3 and General Adapter contract addresses are listed on the [Morpho addresses page](https://docs.morpho.org/get-started/resources/addresses/); the Morpho UI builds the calldata for you.

#### Withdraw

{% stepper %}
{% step %}
**Withdraw a specific asset amount.**

Call `withdraw(assets, receiver, owner)` on the KPK vault:

* `assets` = the asset amount to withdraw
* `receiver` = the address that should receive the underlying asset
* `owner` = the address whose shares should be burned (the depositor, or any address that has approved the caller)
  {% endstep %}
  {% endstepper %}

Or by share amount:

{% stepper %}
{% step %}
**Redeem a specific share amount.**

Call `redeem(shares, receiver, owner)` on the KPK vault:

* `shares` = the share amount to redeem
* `receiver`, `owner` = same as above
  {% endstep %}
  {% endstepper %}

#### Claim rewards

Merkl-distributed reward tokens are claimed via the Merkl distributor:

* Call `claim(users, tokens, amounts, proofs)` on the Merkl distributor, with the values returned by the [Merkl API](https://docs.merkl.xyz/) for the depositor's address.

### Fee wrapper for distribution partners

Distribution partners can request KPK to deploy a [Morpho Fee Wrapper](https://docs.morpho.org/build/earn/concepts/fee-wrapper) on top of a curated vault. This lets the partner charge their users a fee on the underlying KPK strategy without changing the vault itself. Wrapper deployment is a **free service** on KPK's side.

A fee wrapper is a fully ERC-4626 compliant vault that sits on top of the underlying KPK vault. Users deposit into the wrapper, which deposits 1:1 into the selected KPK vault. Yield accrues automatically through share appreciation. The partner's fee is deducted before user withdrawals.

<figure><picture><source srcset="/files/witN71Jgh4E0DKTqfWJ6" media="(prefers-color-scheme: dark)"><img src="https://2037261023-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fb7kueLGoREgSbtMhzWGT%2Fuploads%2F4A9lQnA3XnBPSRApSdLG%2FMORPHOV2_L.png?alt=media&amp;token=cac3615c-24b9-44d2-a702-08fdf1ba14dc" alt=""></picture><figcaption></figcaption></figure>

**What partners get:**

* A standalone ERC-4626 vault, configured with a chosen fee and a recipient address. Supports any standard integration (`deposit`, `withdraw`, `redeem`, `preview`).
* **Flexible fee configuration.** The fee can be a **performance fee** (charged on yield), a **management fee** (charged on AUM), or a combination. Rates and recipient address are set at deployment.
* **Optional permissioned setup.** Wrappers can be deployed with gates on deposit, transfer, and withdrawal to comply with jurisdictional requirements (e.g. KYC-gated access, allowlisted depositors). KPK and the partner agree the gating policy at deployment.
* **Non-custodial guarantees.** Once deployed, gates that could restrict user withdrawals are permanently abdicated, so neither KPK nor the partner can block exits beyond the agreed policy.
* **APY queryable via Morpho.** The wrapper's APY is exposed through the [Morpho GraphQL API](https://docs.morpho.org/tools/offchain/api/morpho-vaults/#feewrapper-yield), already net of the configured fee.

#### What to send when requesting a wrapper

To deploy, KPK needs three details from the partner:

1. **Fee recipient address.** The wallet that will receive performance and/or management fees, in `0x...` format. Any address the partner controls works; KPK can also spin up a fresh Gnosis Safe and transfer ownership. The address must be able to call `redeem()` on the wrapper to convert accrued fee shares to assets.
2. **Custody mode.** Either:
   * **Non-custodial mode** (recommended for DeFi-native deployments). The gate setters (`receiveShares`, `sendShares`, `receiveAssets`) are permanently locked at deployment, so no one can restrict deposits, withdrawals, or transfers afterwards.
   * **Configurable gates.** The owner/curator retains the ability to set gates later for compliance (e.g. KYC/AML allowlists). Configurable gates may introduce custodial characteristics depending on the policy.
3. **Vault naming.**
   * **Vault name** (max 60 characters), e.g. `Partner USDC Yield by KPK`.
   * **Vault symbol** (max 30 characters), the onchain ticker, e.g. `partnerUSDCy`.

Either way, the wrapper does not appear in the public Morpho UI; it is reachable only through the partner's integration or via direct contract calls.

#### Sensible defaults

Partners without a strong preference can ask KPK to proceed with:

* **Non-custodial mode** (standard for DeFi-native deployments).
* **A Fee Recipient Safe** that KPK spins up on the partner's behalf, with the partner's chosen signers; ownership transferable later.
* **Naming** in the form `<Partner> <Asset> <Strategy> by KPK` for the vault name, and `<partner><asset><Strategy>` for the symbol. All three can be changed at deployment time.

To request a wrapper, contact <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact) with the underlying vault, fee structure, and any required gating.

### Support

For integration questions, technical reviews, or hands-on help, reach out to <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact). For protocol-level Morpho documentation, see [docs.morpho.org](https://docs.morpho.org/).


# Gearbox

This page covers the Gearbox-specific integration flows for KPK Earn Pools: direct ERC-4626 calls with the KPK referral code, the Gearbox TypeScript SDK, and supporting reference material. For the general integrator overview and audience-routing, start at the [Integration page](/vaults/integration/integration).

End-users who simply want to deposit should use the [Gearbox UI](https://kpk.gearbox.finance/) directly. Pool addresses, supported assets, and current parameters are listed on each [pool page](/vaults/vaults/gearbox).

{% hint style="info" %}
The guide uses wstETH and the `kpkwstETH` Earn Pool as the example.
{% endhint %}

### Integration paths

KPK pools on Gearbox are **ERC-4626 passive lending pools**. Unlike Morpho or Euler, Gearbox pools do not allocate across multiple underlying markets: the pool lends a single asset to Gearbox borrowers through the Credit Account system, and the integrator-facing surface is the pool itself.

Two integration paths:

* **Direct ERC-4626 calls** with `depositWithReferral`. Simple, two transactions, and ensures KPK is correctly attributed via the referral code (`4143`). This is the recommended path for most integrators.
* **Gearbox TypeScript SDK.** Higher-level access to pool data, market discovery, and transaction construction. Useful if you also need market metadata (supply rate, total assets, available liquidity) or plan to support multiple Gearbox pools.

For pool discovery, KPK-curated Earn Pools are listed on the [pool directory](/vaults/vaults/gearbox) and can also be retrieved onchain via the Gearbox Market Configurator.

### Direct ERC-4626 integration

The recommended deposit path is `depositWithReferral`, which is the standard ERC-4626 `deposit` plus a referral code that attributes the deposit to KPK.

#### Deposit

{% stepper %}
{% step %}
**Approve the pool to spend wstETH.**

Call [`approve(address, uint256)`](https://etherscan.io/address/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0#writeProxyContract#F1) on wstETH (`0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0`):

* `spender(address)` = `0xa9d17f6d3285208280a1fd9b94479c62e0aaba64`, i.e. [`kpkwstETH`](/vaults/vaults/gearbox/ethereum/wsteth)
* `value(amount)` = token amount to deposit
  {% endstep %}

{% step %}
**Deposit with the KPK referral code.**

Call [`depositWithReferral(uint256, address, uint256)`](https://etherscan.io/address/0xA9d17f6D3285208280a1Fd9B94479c62e0AABa64#writeContract#F4) on [`kpkwstETH`](/vaults/vaults/gearbox/ethereum/wsteth):

* `assets(uint256)` = tokens to be deposited
* `receiver(address)` = your address, or the address to which you want the receipt tokens sent
* `referralCode(uint256)` = `4143`, i.e. the KPK referral code
  {% endstep %}
  {% endstepper %}

The plain `deposit(assets, receiver)` ERC-4626 method also works if you cannot pass a referral code, but `depositWithReferral` is preferred so KPK is correctly attributed in Gearbox analytics.

#### Withdraw

{% stepper %}
{% step %}
**Withdraw a specific asset amount.**

Call [`withdraw(uint256, address, address)`](https://etherscan.io/address/0xA9d17f6D3285208280a1Fd9B94479c62e0AABa64#writeContract#F23) on [`kpkwstETH`](/vaults/vaults/gearbox/ethereum/wsteth):

* `assets(uint256)` = amount of wstETH to withdraw
* `receiver(address)` = the address that should receive the wstETH (or WETH, depending on configuration)
* `owner(address)` = the address whose shares should be burned (the depositor, or any address that has approved the caller)
  {% endstep %}
  {% endstepper %}

Or by share amount:

{% stepper %}
{% step %}
**Redeem a specific share amount.**

Call `redeem(shares, receiver, owner)` on the pool.
{% endstep %}
{% endstepper %}

### TypeScript SDK integration

The [`@gearbox-protocol/sdk`](https://docs.gearbox.finance/dev/sdk-guide-typescript/setup) package exposes pool state and transaction helpers. It is the right choice if your integration needs to read live pool metrics (supply rate, total assets, available liquidity) or supports multiple Gearbox pools at once.

#### Install and instantiate

```bash
npm install @gearbox-protocol/sdk viem
```

```typescript
import { GearboxSDK } from '@gearbox-protocol/sdk';
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';

const client = createPublicClient({ chain: mainnet, transport: http() });

const sdk = await GearboxSDK.attach({
  client,
  marketConfigurators: [], // empty array auto-discovers all markets
});
```

#### Read pool state

Find a KPK Earn Pool by its address and read its key metrics:

```typescript
const market = sdk.marketRegister.findByPool(
  '0xa9d17f6d3285208280a1fd9b94479c62e0aaba64', // kpkwstETH
);
const pool = market.pool;

// Supply APY (RAY-scaled supplyRate, converted to a percentage)
const RAY = 10n ** 27n;
const supplyAPY = Number((pool.supplyRate * 10000n) / RAY) / 100;

console.log(`Supply APY: ${supplyAPY}%`);
console.log(`Total assets: ${pool.totalAssets}`);
console.log(`Available liquidity: ${pool.availableLiquidity}`);
```

For deposits and withdrawals, the SDK currently focuses on Credit Account flows for borrowers. For passive lending into a KPK Earn Pool, use the direct ERC-4626 calls described above with the referral code; the SDK is most useful for the surrounding read-side state.

### Claim rewards

KPK Earn Pools may distribute additional GEAR or other incentive tokens via Merkl. Claim them with:

* Call [`claim(users, tokens, amounts, proofs)`](https://etherscan.io/address/0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae#writeProxyContract#F1) on the Merkl distributor, with the values returned by the [Merkl API](https://docs.merkl.xyz/) for the depositor's address.

The claim is a separate transaction and does not modify the user's deposited position.

### Reference

* **Gearbox developer docs:** [docs.gearbox.finance/dev](https://docs.gearbox.finance/dev).
* **TypeScript SDK setup:** [docs.gearbox.finance](https://docs.gearbox.finance/dev/sdk-guide-typescript/setup).
* **TypeScript SDK reading data:** [docs.gearbox.finance](https://docs.gearbox.finance/dev/sdk-guide-typescript/reading-data).
* **Merkl distributor API:** [docs.merkl.xyz](https://docs.merkl.xyz/).

### Support

For integration questions, technical reviews, or hands-on help, reach out to <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact). For protocol-level Gearbox documentation, see [docs.gearbox.finance](https://docs.gearbox.finance/).


# Euler

This page covers the Euler-specific integration flows for KPK vaults: direct ERC-4626 calls, the EVK batch flow with the Ethereum Vault Connector (EVC), and supporting reference material. For the general integrator overview and audience-routing, start at the [Integration page](/vaults/integration/integration).

End-users who simply want to deposit should use the Euler UI directly ([KPK USDC Prime RWA](https://app.euler.finance/earn/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245?network=ethereum)). Vault addresses, supported assets, and current parameters are listed on each [vault page](/vaults/vaults/euler).

{% hint style="info" %}
The example uses USDC and the KPK USDC Prime RWA vault (onchain symbol `KPK_USDC_Prime_RWA`).
{% endhint %}

### Integration paths

KPK vaults on Euler are **ERC-4626** and integrate through one of two paths:

* **Direct ERC-4626 calls.** Standard `approve` + `deposit` (or `withdraw` / `redeem`) against the vault contract. Simple, audit-friendly, two transactions per deposit.
* **EVK batch via the EVC.** Bundles approval and deposit into a single atomic operation through the [Ethereum Vault Connector](https://docs.euler.finance/developers/evc/integration-guide#1-batching-operations). Recommended when you can support [Permit2](https://github.com/Uniswap/permit2) signatures, which remove the need for a separate ERC-20 approval transaction.

For vault discovery, KPK-curated Euler Earn vaults are listed on the [vault directory](/vaults/vaults/euler) and can also be retrieved onchain through Euler's `eulerEarnGovernedPerspective` contract by calling `verifiedArray()`. For offchain data (TVL, deposits, user positions), use Euler's [Euler Earn subgraphs](https://docs.euler.finance/developers/euler-earn).

### Direct ERC-4626 integration

The simplest deposit path is a two-transaction ERC-4626 flow against the KPK vault.

#### Deposit

{% stepper %}
{% step %}
**Approve the vault to spend USDC.**

Call [`approve(spender, amount)`](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48#writeProxyContract#F1) on USDC (`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`):

* `spender` = the KPK vault address (`0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245` for KPK USDC Prime RWA)
* `amount` = the deposit amount, in token units
  {% endstep %}

{% step %}
**Deposit into the vault.**

Call [`deposit(assets, receiver)`](https://etherscan.io/address/0x2b47c128b35dddcb66ce2fa5b33c95314a7de245#writeContract#F6) on the KPK vault (`KPK_USDC_Prime_RWA`):

* `assets` = the deposit amount
* `receiver` = the address that should receive the vault shares (typically the depositor)
  {% endstep %}
  {% endstepper %}

#### Withdraw

{% stepper %}
{% step %}
**Withdraw a specific asset amount.**

Call [`withdraw(assets, receiver, owner)`](https://etherscan.io/address/0x2b47c128b35dddcb66ce2fa5b33c95314a7de245#writeContract#F30) on the KPK vault:

* `assets` = the asset amount to withdraw
* `receiver` = the address that should receive USDC
* `owner` = the address whose shares should be burned (the depositor, or any address that has approved the caller)
  {% endstep %}
  {% endstepper %}

Or by share amount:

{% stepper %}
{% step %}
**Redeem a specific share amount.**

Call `redeem(shares, receiver, owner)` on the KPK vault.
{% endstep %}
{% endstepper %}

### EVK batch integration

For atomic multi-step operations (e.g. Permit2 + deposit in a single transaction), use Euler's EVC `batch()` flow. The EVC composes a sequence of calls that revert as a unit if any step fails, which is useful for any flow that needs to combine an approval with a deposit, or to deposit into multiple vaults at once.

The Euler UI generates the calldata for these batches; integrators that build their own UI should consult the [EVC integration guide](https://docs.euler.finance/developers/evc/integration-guide) for the call structure.

{% stepper %}
{% step %}
**Approve Permit2 to spend USDC** (one-time, gasless via signature on subsequent flows).

Call [`approve(spender, amount)`](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48#writeProxyContract#F1) on USDC:

* `spender` = `0x000000000022D473030F116dDEE9F6B43aC78BA3` (Permit2)
* `amount` = the deposit amount, or `type(uint256).max` for an unlimited approval
  {% endstep %}

{% step %}
**Submit the batch.**

Call [`batch(items)`](https://etherscan.io/address/0x0C9a3dd6b8F28529d72d7f9cE918D493519EE383#writeProxyContract#F1) on the EVC:

* `items` = the ordered array of calldata to execute (Permit2 transfer, vault deposit, etc.). The Euler UI builds this array; if you build your own, follow the [EVC batch documentation](https://docs.euler.finance/developers/evc/integration-guide#1-batching-operations).
  {% endstep %}
  {% endstepper %}

The same pattern applies to withdrawals: call `batch()` with calldata that invokes `withdraw` or `redeem` on the vault.

### Claim rewards

Some KPK vaults on Euler distribute additional incentive tokens via Merkl. Claim them with:

* Call [`claim(users, tokens, amounts, proofs)`](https://etherscan.io/address/0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae#writeProxyContract#F1) on the Merkl distributor, with the values returned by the [Merkl API](https://docs.merkl.xyz/) for the depositor's address.

The claim is a separate transaction and does not modify the user's deposited position.

<details>

<summary><strong>Using the Euler UI</strong></summary>

For integrators that prefer to build their calldata through the Euler UI rather than constructing it manually, the Euler UI ([KPK USDC Prime RWA](https://app.euler.finance/earn/0x2B47c128b35DDDcB66Ce2FA5B33c95314a7de245?network=ethereum)) prompts the same transactions described above. The flows below are kept for reference; the steps match the **EVK batch integration** section.

#### Deposit

The Euler UI will prompt you to execute these transactions in order to deposit USDC into the vault. The approval can be done through a [Permit2](https://github.com/Uniswap/permit2) message or manually.

Call approve(address, uint256) on the USDC token.spender(address) = 0x000000000022D473030F116dDEE9F6B43aC78BA3 (Permit2; a one-time signature to skip re-approvals on future deposits)value(amount) = token amount to depositCall batch(tuple\[]) on Euler's EVK.bundle(tuple\[]) = the ordered array of calldata to execute, created by the Euler UI

Alternatively, deposit USDC into the vault by executing the following transaction order:

Call approve(address, uint256) on USDC.spender(address) = 0x2b47c128b35dddcb66ce2fa5b33c95314a7de245, i.e. KPK\_USDC\_Prime\_RWA value(amount) = token amount to depositCall deposit(uint256, address) on KPK\_USDC\_Prime\_RWA.assets(uint256) = token amount to depositreceiver(address) = your address, or the address to which you want the receipt tokens sent

#### Withdraw

To initiate a withdrawal from the vault, use either of the following functions.

As suggested by the Euler UI:

Call batch(tuple\[]) on Euler's EVK.bundle(tuple\[]) = the ordered array of calldata to execute, created by the Euler UI

Alternatively, call a direct withdrawal from the `KPK_USDC_Prime_RWA` contract:

Call withdraw(uint256, address, address) on KPK\_USDC\_Prime\_RWA.assets(uint256) = amount to withdrawreceiver(address) = address to receive USDCowner(address) = owner of the KPK\_USDC\_Prime\_RWA position

</details>

### Reference

* **Euler Earn integrator guide:** [docs.euler.finance](https://docs.euler.finance/developers/euler-earn/integrator-guide).
* **EVC integration guide:** [docs.euler.finance](https://docs.euler.finance/developers/evc/integration-guide).
* **Permit2:** [github.com/Uniswap/permit2](https://github.com/Uniswap/permit2).
* **Contract addresses across networks:** [docs.euler.finance](https://docs.euler.finance/developers/contract-addresses).
* **Euler Earn subgraphs:** [docs.euler.finance](https://docs.euler.finance/developers/euler-earn).

### Support

For integration questions, technical reviews, or hands-on help, reach out to <sales@karpatkey.com> or use the [contact form](https://kpk.io/contact). For protocol-level Euler documentation, see [docs.euler.finance](https://docs.euler.finance/).


# Glossary

This page provides a list of common terms and definitions used throughout this documentation to explain key concepts.

<table><thead><tr><th width="203.32421875">Defined Term</th><th>Definition</th></tr></thead><tbody><tr><td><strong>Adapter</strong></td><td>A Symbiotic V2 component connecting a vault to a single external venue or mechanism. Each adapter carries an absolute limit in deposit-token units and a relative limit as a share of vault assets, whichever is lower binding. KPK’s Symbiotic vault runs Morpho Vault V2, Aave V3 and Liquid Lane adapters.</td></tr><tr><td><strong>Atomic liquidity</strong></td><td>The share of a vault that can be withdrawn immediately: the vault’s own balance plus whatever can be pulled back from underlying venues on demand. Distinct from the nominal allocation cap, and the measure KPK uses to decide how much of a Symbiotic vault may sit in higher-yielding venues.</td></tr><tr><td><strong>Allocator role</strong></td><td>A protocol-level role (Morpho, Euler) authorised to move funds between enabled markets. KPK does not use the public Allocator role on either protocol; allocation is scoped through KPK's <strong>Permissions Layer</strong> and the protocol's own role system to reduce the attack surface.</td></tr><tr><td><strong>Change Log</strong></td><td>A record of all major changes implemented in KPK-curated products. Each protocol section in this handbook has a dedicated Change Log for full transparency.</td></tr><tr><td><strong>Collateral</strong></td><td>Assets accepted within curated products to back borrowing or leveraged positions. Collateral parameters (e.g. liquidation thresholds, quotas, oracles) are typically set and/or monitored by KPK.</td></tr><tr><td><strong>Credit Account</strong></td><td>A Gearbox primitive: a per-borrower smart account that holds collateral and borrowed liquidity from a KPK Earn Pool and executes leveraged strategies under whitelisted adapters and parameters.</td></tr><tr><td><strong>Curator Safe</strong></td><td>The KPK multisig (typically 2/5) that holds Curator and Allocator roles on a vault. Day-to-day operations are scoped through KPK's Permissions Layer for agents; sensitive changes execute via the Safe under timelock.</td></tr><tr><td><strong>Delegator</strong></td><td>The Symbiotic V2 component holding a vault’s ordered list of adapters and the limits on each. It is owned by the vault itself. Allocation runs through it via auto-allocate, which fills adapters in list order up to their caps.</td></tr><tr><td><strong>Earn Pools</strong></td><td>KPK-curated passive pools on <a href="/vaults/vaults/gearbox">Gearbox Permissionless (v3.1)</a>.</td></tr><tr><td><strong>Emergency Admin</strong></td><td>A predefined role with the ability to take immediate protective actions (e.g. pausing markets) in response to critical alerts, bypassing timelocks.</td></tr><tr><td><strong>EVC (Ethereum Vault Connector)</strong></td><td>The Euler component that coordinates collateral, liability and liquidation flows across EVK vaults, allowing a single account to hold positions in multiple Euler vaults.</td></tr><tr><td><strong>EVK (Euler Vault Kit)</strong></td><td>The Euler primitive used to instantiate ERC-4626 credit vaults with borrowing functionality. Each EVK vault has its own caps, oracle routing and risk parameters.</td></tr><tr><td><strong>Fact Sheet</strong></td><td>The one-pager summary of each KPK-curated product, provided on the relevant product page in this handbook, as well as the KPK website.</td></tr><tr><td><strong><code>forceDeallocate</code></strong></td><td>A permissionless function on Morpho v2 vaults that pulls liquidity from a specific underlying market back to the vault's idle balance. Each call burns a small per-adapter penalty from the caller's vault shares to discourage misuse. KPK keeps the penalty above zero to preserve allocation policy.</td></tr><tr><td><strong>KPK</strong></td><td>For the purpose of all KPK-curated products, KPK Digital Assets Inc.</td></tr><tr><td><strong>KPK-curated Products</strong></td><td>Pools and vaults hosted by leading DeFi protocols that bring together a diverse range of yield sources and risk profiles, where KPK manages the selection of the underlying assets (collateral) and allocation of funds to “curate” the product.</td></tr><tr><td><strong>Liquidation Threshold or LT</strong></td><td>The collateral ratio below which a position becomes liquidatable (e.g. on Gearbox). LT is set per collateral asset and determines borrowing capacity and liquidation risk.</td></tr><tr><td><strong>Liquid Lane</strong></td><td>Symbiotic’s request-for-quote mechanism for providing instant withdrawal liquidity on tokenised RWAs. A curator states in advance the discount at which it will acquire a given asset; the vault buys a holder’s position at that discount and redeems it with the issuer at par on the normal settlement schedule. See <a href="/vaults/vaults/symbiotic">Symbiotic</a>.</td></tr><tr><td><strong>Liquidity adapter</strong></td><td>A per-vault setting on Morpho v2 vaults designating a single underlying market as the default withdrawal source. Withdrawals beyond idle pull from that one market until either the request is filled or that market's available liquidity is exhausted.</td></tr><tr><td><strong>Market Configurator</strong></td><td>The smart contract through which KPK configures parameters (e.g. collaterals, oracles, debt limits) for curated products on Gearbox, typically controlled through timelocked governance.</td></tr><tr><td><strong>Markets V1 Adapter</strong></td><td>A Morpho v2 adapter that allocates a v2 vault's deposits directly into individual Morpho v1 markets. KPK v2 vaults use this adapter to access v1 market liquidity without an intermediate v1 MetaMorpho vault. See <a href="https://docs.morpho.org/get-started/resources/contracts/morpho-market-v1-adapter-v2#morphomarketv1adapterv2">Morpho docs</a>.</td></tr><tr><td><strong>MetaMorpho Vault</strong></td><td>A Morpho v1 vault standard: an ERC-4626 vault that allocates across approved Morpho v1 markets. KPK still operates legacy v1 MetaMorpho vaults for direct V1 depositors; new curation is delivered on v2 vaults.</td></tr><tr><td><strong>Minimum discount</strong></td><td>The smallest discount to fair value at which a Liquid Lane vault will acquire a given RWA. Set per asset by the curator, it is the compensation required for holding the asset until issuer settlement, and together with the per-asset acquisition limit it bounds the vault’s RWA exposure.</td></tr><tr><td><strong>Oracle</strong></td><td>A data feed that provides price information to smart contracts. Oracles are monitored for liveness, accuracy, and manipulation risk.</td></tr><tr><td><strong>Roles Modifier</strong></td><td>A <a href="https://docs.roles.gnosisguild.org/">Zodiac</a> module enabled on each Curator Safe that checks every call against a role’s allow-list before the Safe executes it, scoped by target contract, function, and parameter value. Non-permitted calls revert onchain. It is the enforcement mechanism behind KPK’s <strong>Permissions Layer</strong>. See <a href="/vaults/infrastructure/signer-architecture">Signer architecture</a>.</td></tr><tr><td><strong>Permissions Layer</strong></td><td>KPK’s permission framework, built on the <strong>Roles Modifier</strong>. It strictly scopes the actions curator multisigs and agents can execute, by target contract, function, and parameter value, adding a layer of control on top of protocol-level role restrictions. <a href="https://kpk.io/blog/permissions-layer">See more</a>.</td></tr><tr><td><strong>Quota</strong></td><td>A Gearbox-specific parameter that limits the total amount of a given collateral that can be borrowed against in a curated market. Quotas are set by KPK to control exposure.</td></tr><tr><td><a href="/vaults/infrastructure/risk-framework"><strong>Risk Framework</strong></a></td><td>KPK's proprietary system for evaluating, classifying and monitoring risks that may apply to KPK-curated products.</td></tr><tr><td><a href="/vaults/infrastructure/risk-framework#tiering-and-allocation"><strong>Risk Tier</strong></a></td><td>A KPK-assigned classification reflecting the risk profile of an asset or strategy considered for inclusion in any KPK-curated product.</td></tr><tr><td><strong>Timelock</strong></td><td>A governance mechanism that enforces a minimum delay between the proposal and execution of critical changes, improving transparency and security.</td></tr><tr><td><strong>Utilisation Rate</strong></td><td>The share of total pool liquidity that is borrowed. High utilisation rates can affect withdrawal availability and borrowing costs.</td></tr><tr><td><strong>Vault</strong></td><td>A smart contract that aggregates deposits and manages funds according to a predefined strategy. KPK Morpho and Euler vaults are ERC-4626 vaults that allocate across underlying isolated lending markets; KPK Gearbox pools are ERC-4626 passive lending pools that lend to the Gearbox Credit Account system; KPK Symbiotic vaults are ERC-4626 compatible and allocate across adapters rather than markets.</td></tr><tr><td><strong>Vault V1 Adapter</strong></td><td>A legacy Morpho v2 adapter that routed v2 vault deposits into a single underlying v1 MetaMorpho vault. KPK v2 vaults migrated off this adapter in May 2026 to allocate directly into individual v1 markets via the Markets V1 Adapter.</td></tr></tbody></table>


# FAQs

This section provides detailed answers to a range of common questions relating to KPK-curated products.

<details>

<summary><strong>How do fees on KPK-curated products work?</strong></summary>

Some protocols allow curators to charge fees. Where KPK applies such fees, they’re clearly stated on both the protocol’s interface and the relevant product page in this handbook. The exact mechanism depends on the underlying protocol.

For example, on Gearbox, interest fees are only charged to borrowers in the underlying lending markets. Depositors in KPK Earn Pools don’t pay any protocol or curator fees by default. Unless otherwise stated, curator fees (when applicable) are taken as a share of earned interest and are handled automatically within the product’s smart contracts.

</details>

<details>

<summary><strong>How are changes to KPK-curated products managed?</strong></summary>

All configuration changes are executed through **KPK’s multi-signature wallets,** following strict, auditable governance procedures. Because configuration updates are a key security vector, these changes are often subject to **timelocks** and predefined permissions to ensure transparency and control.

Most protocols we work with already implement **role-based restrictions** such as timelocks for sensitive functions and layered permission systems. On top of these, **KPK applies its proprietary** [**Permissions Layer**](https://kpk.io/blog/permissions-layer), which tightly scopes the actions curator multisigs can perform. This added layer ensures that only pre-approved transactions within well-defined parameters can be executed, significantly reducing the attack surface.

All changes are publicly visible onchain and documented in each product’s Change Log for full traceability.

</details>

<details>

<summary><strong>How often are KPK-curated products reviewed and updated?</strong></summary>

Before launch, each product undergoes a thorough due diligence process. Once live, products and their underlying assets are continuously monitored, with **risk alerts tracked in real time and daily reviews** to assess anomalies or market changes.

Performance is reviewed on a **daily and weekly basis** to spot gaps or opportunities. High-level parameters such as liquidation thresholds or oracle configurations are typically stable and rarely adjusted more than once a month. Lower-level settings, like individual market allocations, may change more frequently in response to market dynamics. KPK can act within seconds when conditions require, **combining predictability for users** with **responsiveness to risk.**

</details>

<details>

<summary><strong>What security monitoring is in place?</strong></summary>

KPK combines **automated alerts, protocol-level monitors, and manual oversight** to protect curated products.

Every collateral and vault is covered by a bespoke set of monitors, including oracle liveness checks, price deviation alerts, governance proposal tracking, contract upgrade detection, and insolvency monitoring. We use third-party services such as **Hypernative** alongside in-house systems to ensure broad coverage.

For our Gearbox markets, an *Insolvency Monitor* continuously checks whether discounted collateral values exceed outstanding debt. If thresholds are breached, borrowing can be paused within seconds through the Emergency Admin role. Similar frameworks are applied across other integrated protocols.

</details>

<details>

<summary><strong>What due diligence does KPK perform?</strong></summary>

Before an asset or strategy is included in a curated product, KPK applies a structured [Curation Risk Framework](/vaults/infrastructure/risk-framework) that evaluates smart contract design, governance, oracle structure, liquidity, and broader market behaviour. This combines onchain and offchain analysis with external risk signals from platforms such as Credora, Exponential, and Pharos Watch.

The process continues after inclusion through ongoing monitoring, ensuring any material changes in risk are detected and addressed promptly. For more information, see the [Curation Risk Framework](/vaults/infrastructure/risk-framework).

</details>

<details>

<summary><strong>Do you provide direct access to KPK-curated products?</strong></summary>

No. All curated products are accessed **through the user interfaces of the underlying protocols**. KPK does not operate separate frontends or distribution channels.

Be cautious of any third-party sites claiming to offer direct access to KPK-curated products.

</details>

<details>

<summary><strong>How can my organisation integrate with KPK’s products?</strong></summary>

All KPK-curated products are permissionless: **integrators can interact directly through the protocols’ interfaces or smart contracts.**

For partners, KPK can design and deploy **custom lending markets or vaults**, helping protocols scale liquidity, broaden collateral coverage, or launch specialised strategies. To explore partnership opportunities, reach out via the [contact form](https://kpk.io/contact/) on our website.

</details>


# Legal

Disclaimers covering the terms on which KPK-curated vaults and pools are offered to users. See the per-protocol pages: [Gearbox Disclaimer](/vaults/resources/legal/gearbox-disclaimer), [Euler Disclaimer](/vaults/resources/legal/euler-disclaimer), [Morpho Disclaimer](/vaults/resources/legal/morpho-disclaimer), and [Symbiotic Disclaimer](/vaults/resources/legal/symbiotic-disclaimer).


# Morpho disclaimer

The vaults curated by KPK Digital Assets Inc. (“KPK”) within the Morpho Protocol (“Vaults”) are provided on a discretionary, non-custodial, and permissionless basis through smart contracts. By interacting with any Vault curated by KPK, you acknowledge and agree to the following:

#### No offer or advice

* The information, parameters, and strategies applied to the Vaults are provided for general informational purposes only.
* Nothing herein constitutes financial, investment, legal, tax, accounting, or other professional advice.
* Vaults curated by KPK do not constitute an offer to sell, solicitation of an offer to buy, or recommendation of any financial instrument, investment product, or security.
* KPK is not acting as your broker, investment adviser, fiduciary, or asset manager.

#### No custody or guarantee

* KPK does not hold, control, or take custody of your assets. All deposits and withdrawals occur directly through Morpho smart contracts, which are open-source and non-custodial.
* KPK’s services are provided “as is” without warranties of any kind; more in particular, KPK provides no assurance or guarantee as to the safety of assets deployed into the Vaults, the accuracy of parameters, or the future performance of the strategies.
* Where Vault strategies involve exposure to real-world assets, KPK does not control, manage, or take any responsibility for the performance, enforceability, valuation, liquidity, redeemability, or legal integrity of the underlying off-chain assets or the entities issuing, managing, or servicing them. The on-chain representation of an asset does not guarantee the existence, quality, or recoverability of the underlying off-chain asset.
* Vault users remain solely responsible for the custody and security of their private keys and digital assets.

#### Market and protocol risks

* Interacting with Vaults involves significant risks, including but not limited to: extreme market volatility, liquidation risk, smart contract vulnerabilities, governance changes, regulatory developments, and loss of all or part of your assets.
* Past performance of any strategy does not guarantee future results.
* Strategies are experimental, may be changed or discontinued at any time, and may expose users of KPK-curated Products to sudden losses or illiquidity.

#### Prohibited persons and jurisdictions

* Vaults curated by KPK are not registered with, or approved by, any financial regulatory authority in any jurisdiction.
* Vaults curated by KPK are not available to, and must not be accessed by, persons or entities located, incorporated, or resident in jurisdictions where or to whose residents or nationals such access or use is restricted or prohibited (including, without limitation, jurisdictions subject to comprehensive sanctions, such as those administered by OFAC).
* By using a Vault, you represent that you are not a prohibited person and that you are acting in compliance with applicable laws.

#### Limitation of liability

* KPK’s sole role is exclusively to set Vault parameters within Morpho. KPK has no contractual obligations to Vault users and accepts no responsibility for any losses, damages, or claims arising out of or in connection with the Vaults. In particular, KPK shall bear no liability in connection with: (i) the insolvency or default of any off-chain issuer or asset servicer; (ii) the failure or inaccuracy of any third-party valuation, oracle, or data provider; (iii) the legal unenforceability of any interest in underlying real-world assets; or (iv) any regulatory action taken against the issuer or the underlying assets in any jurisdiction.
* To the maximum extent permitted by law, you waive any rights or remedies against KPK and agree that KPK shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from your use of the Vaults, including those caused by user error, blockchain malfunctions (e.g., forks, attacks), changes in law or force majeure events (incl. cyber attacks, network failures).
* Your only contractual rights and remedies, if any, arise under the Morpho Protocol’s Terms of Service.


# Gearbox disclaimer

The products curated by KPK Digital Assets Inc. (“KPK”) within the Gearbox Protocol (“KPK-curated Products”) are provided on a discretionary, non-custodial, and permissionless basis through smart contracts. By interacting with any KPK-curated Products, you acknowledge and agree to the following:

#### No offer or advice

* The information, parameters, and strategies applied to the KPK-curated Products are provided for general informational purposes only.
* Nothing herein constitutes financial, investment, legal, tax, accounting, or other professional advice.
* KPK-curated Products do not constitute an offer to sell, solicitation of an offer to buy, or recommendation of any financial instrument, investment product, or security.
* KPK is not acting as your broker, investment adviser, fiduciary, or asset manager.

#### No custody or guarantee

* KPK does not hold, control, or take custody of your assets. All deposits and withdrawals occur directly through Gearbox smart contracts, which are open-source and non-custodial.
* KPK’s services are provided “as is” without warranties of any kind; more in particular, KPK provides no assurance or guarantee as to the safety of assets deployed into the KPK-curated Products, the accuracy of parameters, or the future performance of the strategies.
* Where Product strategies involve exposure to real-world assets, KPK does not control, manage, or take any responsibility for the performance, enforceability, valuation, liquidity, redeemability, or legal integrity of the underlying off-chain assets or the entities issuing, managing, or servicing them. The on-chain representation of an asset does not guarantee the existence, quality, or recoverability of the underlying off-chain asset.
* Users of KPK-curated Products remain solely responsible for the custody and security of their private keys and digital assets.

#### Market and protocol risks

* Interacting with KPK-curated Products involves significant risks, including but not limited to: extreme market volatility, liquidation risk, smart contract vulnerabilities, governance changes, regulatory developments, and loss of all or part of your assets.
* Past performance of any strategy does not guarantee future results.
* Strategies are experimental, may be changed or discontinued at any time, and may expose users of KPK-curated Products to sudden losses or illiquidity.

#### Prohibited persons and jurisdictions

* KPK-curated Products are not registered with, or approved by, any financial regulatory authority in any jurisdiction.
* KPK-curated Products are not available to, and must not be accessed by, persons or entities located, incorporated, or resident in jurisdictions where or to whose residents or nationals such access or use is restricted or prohibited (including, without limitation, jurisdiction subject to comprehensive sanctions, such as those administered by OFAC).
* By using KPK-curated Products, you represent that you are not a prohibited person and that you are acting in compliance with applicable laws.

#### Limitation of liability

* KPK’s role is exclusively to set the parameters of KPK-curated Products within Gearbox. KPK has no contractual obligations to users of the KPK-curated Products and accepts no responsibility for any losses, damages, or claims arising out of or in connection with the KPK-curated Products. In particular, KPK shall bear no liability in connection with: (i) the insolvency or default of any off-chain issuer or asset servicer; (ii) the failure or inaccuracy of any third-party valuation, oracle, or data provider; (iii) the legal unenforceability of any interest in underlying real-world assets; or (iv) any regulatory action taken against the issuer or the underlying assets in any jurisdiction.
* To the maximum extent permitted by law, you waive any rights or remedies against KPK and agree that KPK shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from your use of the KPK-curated Products, including those caused by user error, blockchain malfunctions (e.g., forks, attacks), changes in law or force majeure events (incl. cyber attacks, network failures).
* Your only contractual rights and remedies, if any, arise under the Gearbox Protocol’s Terms of Service.


# Euler disclaimer

The vaults curated by KPK Digital Assets Inc. (“KPK”) within the Euler Protocol (“Vaults”) are provided on a discretionary, non-custodial, and permissionless basis through smart contracts. By interacting with any Vault curated by KPK, you acknowledge and agree to the following:

#### No offer or advice

* The information, parameters, and strategies applied to the Vaults are provided for general informational purposes only.
* Nothing herein constitutes financial, investment, legal, tax, accounting, or other professional advice.
* Vaults curated by KPK do not constitute an offer to sell, solicitation of an offer to buy, or recommendation of any financial instrument, investment product, or security.
* KPK is not acting as your broker, investment adviser, fiduciary, or asset manager.

#### No custody or guarantee

* KPK does not hold, control, or take custody of your assets. All deposits and withdrawals occur directly through Euler smart contracts, which are open-source and non-custodial.
* KPK’s services are provided “as is” without warranties of any kind; more in particular, KPK provides no assurance or guarantee as to the safety of assets deployed into the Vaults, the accuracy of parameters, or the future performance of the strategies.
* Where Vault strategies involve exposure to real-world assets, KPK does not control, manage, or take any responsibility for the performance, enforceability, valuation, liquidity, redeemability, or legal integrity of the underlying off-chain assets or the entities issuing, managing, or servicing them. The on-chain representation of an asset does not guarantee the existence, quality, or recoverability of the underlying off-chain asset.
* Vault users remain solely responsible for the custody and security of their private keys and digital assets.

#### Market and protocol risks

* Interacting with Vaults involves significant risks, including but not limited to: extreme market volatility, liquidation risk, smart contract vulnerabilities, governance changes, regulatory developments, and loss of all or part of your assets.
* Past performance of any strategy does not guarantee future results.
* Strategies are experimental, may be changed or discontinued at any time, and may expose users of KPK-curated Products to sudden losses or illiquidity.

#### Prohibited persons and jurisdictions

* Vaults curated by KPK are not registered with, or approved by, any financial regulatory authority in any jurisdiction.
* Vaults curated by KPK are not available to, and must not be accessed by, persons or entities located, incorporated, or resident in jurisdictions where or to whose residents or nationals such access or use is restricted or prohibited (including, without limitation, jurisdictions subject to comprehensive sanctions, such as those administered by OFAC).
* By using a Vault, you represent that you are not a prohibited person and that you are acting in compliance with applicable laws.

#### Limitation of liability

* KPK’s sole role is exclusively to set Vault parameters within Euler. KPK has no contractual obligations to Vault users and accepts no responsibility for any losses, damages, or claims arising out of or in connection with the Vaults. In particular, KPK shall bear no liability in connection with: (i) the insolvency or default of any off-chain issuer or asset servicer; (ii) the failure or inaccuracy of any third-party valuation, oracle, or data provider; (iii) the legal unenforceability of any interest in underlying real-world assets; or (iv) any regulatory action taken against the issuer or the underlying assets in any jurisdiction.
* To the maximum extent permitted by law, you waive any rights or remedies against KPK and agree that KPK shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from your use of the Vaults, including those caused by user error, blockchain malfunctions (e.g., forks, attacks), changes in law or force majeure events (incl. cyber attacks, network failures).
* Your only contractual rights and remedies, if any, arise under the Euler Protocol’s Terms of Service.


# Symbiotic disclaimer

The vaults curated by KPK Digital Assets Inc. (“KPK”) within the Symbiotic Protocol (“Vaults”) are provided on a discretionary, non-custodial, and permissionless basis through smart contracts. By interacting with any Vault curated by KPK, you acknowledge and agree to the following:

#### No offer or advice

* The information, parameters, and strategies applied to the Vaults are provided for general informational purposes only.
* Nothing herein constitutes financial, investment, legal, tax, accounting, or other professional advice.
* Vaults curated by KPK do not constitute an offer to sell, solicitation of an offer to buy, or recommendation of any financial instrument, investment product, or security.
* KPK is not acting as your broker, investment adviser, fiduciary, or asset manager.

#### No custody or guarantee

* KPK does not hold, control, or take custody of your assets. All deposits and withdrawals occur directly through Symbiotic smart contracts, which are open-source and non-custodial.
* KPK’s services are provided “as is” without warranties of any kind; more in particular, KPK provides no assurance or guarantee as to the safety of assets deployed into the Vaults, the accuracy of parameters, or the future performance of the strategies.
* Where Vault strategies involve exposure to real-world assets, KPK does not control, manage, or take any responsibility for the performance, enforceability, valuation, liquidity, redeemability, or legal integrity of the underlying off-chain assets or the entities issuing, managing, or servicing them. The on-chain representation of an asset does not guarantee the existence, quality, or recoverability of the underlying off-chain asset.
* Vault users remain solely responsible for the custody and security of their private keys and digital assets.

#### Market and protocol risks

* Interacting with Vaults involves significant risks, including but not limited to: extreme market volatility, liquidation risk, smart contract vulnerabilities, governance changes, regulatory developments, and loss of all or part of your assets.
* Past performance of any strategy does not guarantee future results.
* Strategies are experimental, may be changed or discontinued at any time, and may expose users of KPK-curated Products to sudden losses or illiquidity.

#### Prohibited persons and jurisdictions

* Vaults curated by KPK are not registered with, or approved by, any financial regulatory authority in any jurisdiction.
* Vaults curated by KPK are not available to, and must not be accessed by, persons or entities located, incorporated, or resident in jurisdictions where or to whose residents or nationals such access or use is restricted or prohibited (including, without limitation, jurisdictions subject to comprehensive sanctions, such as those administered by OFAC).
* By using a Vault, you represent that you are not a prohibited person and that you are acting in compliance with applicable laws.

#### Limitation of liability

* KPK’s sole role is exclusively to set Vault parameters within Symbiotic. KPK has no contractual obligations to Vault users and accepts no responsibility for any losses, damages, or claims arising out of or in connection with the Vaults. In particular, KPK shall bear no liability in connection with: (i) the insolvency or default of any off-chain issuer or asset servicer; (ii) the failure or inaccuracy of any third-party valuation, oracle, or data provider; (iii) the legal unenforceability of any interest in underlying real-world assets; or (iv) any regulatory action taken against the issuer or the underlying assets in any jurisdiction.
* To the maximum extent permitted by law, you waive any rights or remedies against KPK and agree that KPK shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from your use of the Vaults, including those caused by user error, blockchain malfunctions (e.g., forks, attacks), changes in law or force majeure events (incl. cyber attacks, network failures).
* Your only contractual rights and remedies, if any, arise under the Symbiotic Protocol’s Terms of Service.


# Introduction

KPK Funds are composable onchain funds designed to provide institutional-grade exposure to DeFi yield strategies with non-custodial settlement and onchain accounting.

Funds are deployed on KPK’s onchain fund infrastructure (previously called **Onchain Investment Vehicle (OIV)**).

At launch, Funds will be issued exclusively by KPK’s trading desk as actively managed, tokenised funds. This approach builds on KPK’s track record of managing over $2 billion in onchain treasury assets for DeFi DAOs over the past five years, enabling permissionless access to fund shares designed to optimise risk-adjusted returns.

Key characteristics:

{% columns %}
{% column %}
**Non-custodial**\
Assets are held in smart contracts where no single entity has control, ensuring no party can take custody of the funds.
{% endcolumn %}

{% column %}
**Onchain policies**\
Policy smart contracts define a granular set of pre-approved actions the trading desk can execute on behalf of each fund, aligned to the fund’s investment framework and risk parameters.
{% endcolumn %}
{% endcolumns %}

{% columns %}
{% column %}
**Onchain accounting**\
A fund's Net Asset Value (NAV) is computed onchain by per-chain NAV Calculator contracts, and shares are priced and settled against it — transparent and verifiable.
{% endcolumn %}

{% column %}
**Composability**\
Fund shares are ERC-20 tokens, enabling integration with other DeFi protocols to increase capital efficiency.
{% endcolumn %}
{% endcolumns %}

{% columns %}
{% column %}
**Flexibility**\
Funds are built on Safe smart accounts behind Zodiac Roles Modifiers, retaining the ability to handle a wide range of DeFi assets and transaction payloads to support sophisticated strategies.
{% endcolumn %}

{% column %}
**Multichain**\
Funds can access opportunities across multiple EVM networks while maintaining consistent accounting and security controls.
{% endcolumn %}
{% endcolumns %}

KPK’s fund infrastructure abstracts DeFi’s operational complexity to bridge the composability and transparency of DeFi with the distribution scale of fintech. We foresee the emergence of a connected, hybrid financial system that is programmable, global, and open by default.

Our mission focuses on improving operational efficiency through automation, reducing costs, and creating a safer, more accessible ecosystem for DeFi-native asset management.


# USD Alpha

USD Alpha Fund is a USDC-denominated fund designed to generate yield from delta-neutral DeFi strategies. It is actively managed, but execution is constrained by onchain permissions. Users enter and exit via subscription and redemption requests rather than direct deposits and withdrawals. Pricing is NAV-based, so request settlement reflects the latest accounting across chains. This section collects the fund-specific mandate and constraints, separate from the protocol-wide mechanics.

In this section, you’ll find:

* The fund’s objective, positioning, and high-level design.
* The investment framework and risk process used by the manager.
* The scope of allowed onchain actions expressed as policies.


# Overview

The USD Alpha Fund is a multichain, USDC-denominated fund designed to generate yield from delta-neutral strategies across the DeFi ecosystem. Drawing on experience managing over $2B in assets for DeFi DAO treasuries, KPK applies a structured investment framework and active management approach to control risk and pursue targeted yield outcomes.

### **Income Generation**

The Fund aims to generate risk-adjusted income through a diversified set of DeFi strategies, including lending and borrowing, yield farming, staking, and other protocol-based yield opportunities, as well as select private transactions, in accordance with the Fund’s investment framework.

### **Relative Return**

The Fund seeks to deliver returns, net of fees, in excess of DeFi savings rates' benchmarks through active deployment of capital across delta-neutral strategies.

### **Diversification**

The Fund seeks to manage concentration risk by diversifying exposures across multiple DeFi protocols, blockchain networks, and position types, thereby reducing reliance on any single source of return or risk factor.

### **Capital Preservation**

The Fund prioritises capital preservation through disciplined risk management and allocations to DeFi protocols exhibiting adequate liquidity, solvency characteristics, and operational robustness, with the objective of limiting downside risk across market conditions.


# Investment framework

## **Risk Regime Definition**

The Fund employs a systematic regime-classification model that integrates technical and fundamental variables to distinguish between distinct market risk environments.

The model employs both supervised and unsupervised classification methodologies, incorporating fundamental macro variables such as interest rate expectations, derived from the US Treasury yield curve, and changes in aggregate liquidity, derived from M2 data, as well as market sentiment indicators based on momentum in crypto and traditional equity markets.

Within each market regime, the model decomposes the implied DeFi rate risk premium using a multi-factor framework that incorporates ETH price volatility, supply rate volatility, and leverage indicators. These inputs are used to characterise prevailing market conditions and to group observations into distinct risk clusters.

Once risk clusters are defined, the current market regime is assessed against historical data to evaluate observed patterns in DeFi rate behaviour and corresponding risk premium. The resulting outputs inform the Fund’s risk tolerance parameters and guide decisions related to leverage utilisation, strategy concentration, and protocol-specific risk appetite and capital allocation.

## **Fundamental Risk Assessment**

Alongside market-regime analysis, the Fund applies a structured, layered evaluation framework to each protocol and collateral type considered for allocation decisions.

#### **Collateral Assessment**

Exposure to any collateral, regardless of the platform or protocol used, is central to the fundamental risk assessment. This analysis includes:

* Smart contract design and technological risk;
* Governance structure and stakeholder incentives;
* Collateral quality, including default and depeg risk;
* Liquidity mechanisms and historical behaviour;

#### **Protocol Assessment**

Focuses on the resilience of the protocol layer. The protocol is the application through which the Fund accesses a given market or strategy, with a specific collateral as the underlying asset. This assessment includes both on-chain and off-chain variables:

* Protocol architecture, including composability features, time-locks, and emergency procedures;
* Vault/Market design;
* Oracle mechanisms;
* Protocol Governance;
* External Audits and security reviews;

#### **Blockchain Assessment**

The Fund’s multi-chain approach provides an additional layer of flexibility, ultimately enhancing return opportunities. While this multi-chain approach is constrained to EVM-compatible chains, which promotes interoperability, tooling maturity, and developer standardisation, it does not eliminate underlying blockchain-level risks. Therefore, this layer of assessment includes:

* Network security and consensus mechanisms;
* Validator concentration;
* Historical uptime and incident record;
* Governance structure, including reliance on centralized infrastructure;
* Bridge design and cross-chain dependencies;

## **Allocation Decisions**

A formal rating system is applied to the collateral and protocol assessment layers. The outputs of these two assessments are combined into a composite rating for each collateral–protocol pairing. This composite rating directly determines the permissible allocation range and informs position sizing, leverage constraints, and exposure limits within the Fund.

By contrast, the Blockchain assessment operates in a whitelisting system, whereby a blockchain is either included in or excluded from the Fund’s eligible investment universe. No allocation may be made to protocols or positions deployed on non-approved chains.

### Additional Notes

Leverage may be employed where consistent with the prevailing market risk regime and the outcomes of the fundamental risk assessment. The Fund may also participate in private transactions within the approved protocol universe, subject to counterparties meeting internal risk standards.

This layered evaluation framework is ultimately operationalised through a set of onchain Permission Policies, which enforce the Fund’s investment mandate and risk constraints in accordance with the Investment Framework.


# Policies and addresses

The Investment and Risk Management Frameworks are implemented as onchain [policies](/funds/infrastructure/core-concepts/policies) within the Fund’s dedicated investment vehicle. These policies enforce verifiable constraints on the addresses, functions and parameters the Fund can use, supporting automated execution, transparent operation and enforceable controls across all investment activity.

KPK’s fund infrastructure is non-custodial and deployed across multiple blockchains, comprising a network of Safes with distinct roles. The Portfolio Safe holds Fund assets and defines the permitted action set through onchain policy controls. Manager Safe [operators](/funds/infrastructure/core-concepts/roles-and-operators) execute transactions and workflows, ranging from asset allocation decisions to subscriptions and redemptions requests, with each transaction authorised and parameterised through a role-based onchain permission framework that enforces the Portfolio Safe’s constraints.

Use the fund dashboard’s **Policy** view for the full, up-to-date configuration. For how this Safe stack is assembled and why the addresses are identical across chains, see [Architecture overview](/funds/infrastructure/architecture-overview) and [Deployment](/funds/infrastructure/deployment).

### Addresses

#### Mainnet

<table><thead><tr><th width="218.84765625">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x38F6a1B46144fAEe6a6D9F79D8dE264C18e23848</td></tr><tr><td>Manager Safe</td><td>0x7Bb5e307eDf80630f153BD28789b4365eFe4cce3</td></tr><tr><td>Shares Contract</td><td>0x6D1a4C0878aD24793b1655ae1f78Cfa4522Ba765</td></tr></tbody></table>

#### Arbitrum

<table><thead><tr><th width="218.84765625">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x38F6a1B46144fAEe6a6D9F79D8dE264C18e23848</td></tr><tr><td>Manager Safe</td><td>0x7Bb5e307eDf80630f153BD28789b4365eFe4cce3</td></tr></tbody></table>

#### Base

<table><thead><tr><th width="218.84765625">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x38F6a1B46144fAEe6a6D9F79D8dE264C18e23848</td></tr><tr><td>Manager Safe</td><td>0x7Bb5e307eDf80630f153BD28789b4365eFe4cce3</td></tr></tbody></table>

#### Optimism

<table><thead><tr><th width="218.84765625">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x38F6a1B46144fAEe6a6D9F79D8dE264C18e23848</td></tr><tr><td>Manager Safe</td><td>0x7Bb5e307eDf80630f153BD28789b4365eFe4cce3</td></tr></tbody></table>

#### Gnosis

<table><thead><tr><th width="218.84765625">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x38F6a1B46144fAEe6a6D9F79D8dE264C18e23848</td></tr><tr><td>Manager Safe</td><td>0x7Bb5e307eDf80630f153BD28789b4365eFe4cce3</td></tr></tbody></table>


# ETH Alpha

ETH Alpha Fund is an ETH-denominated fund designed to generate yield from delta-neutral DeFi strategies. It is actively managed, but execution is constrained by onchain permissions. Users enter and exit via subscription and redemption requests rather than direct deposits and withdrawals. Pricing is NAV-based, so request settlement reflects the latest accounting across chains. This section collects the fund-specific mandate and constraints, separate from the protocol-wide mechanics.

In this section, you’ll find:

* The fund’s objective, positioning, and high-level design.
* The investment framework and risk process used by the manager.
* The scope of allowed onchain actions expressed as policies.


# Overview

The ETH Alpha Fund is a multichain, ETH-denominated fund designed to generate yield from delta-neutral strategies across the DeFi ecosystem. Drawing on experience managing over $2B in assets for DeFi DAO treasuries, KPK applies a structured investment framework and active management approach to control risk and pursue targeted yield outcomes.

### **Income Generation**

The Fund aims to generate risk-adjusted income through a diversified set of DeFi strategies, and occasionally select private transactions, in accordance with the Fund’s investment framework.

### **Relative Return**

The Fund seeks to deliver returns, net of fees, in excess of 50 basis points over the ETH re-staking yield.

### **Diversification**

The Fund seeks to manage concentration risk by diversifying exposures across multiple strategies, DeFi protocols, and blockchain networks, thereby reducing reliance on any single source of return or risk factor.

### **Capital Preservation**

The Fund prioritises capital preservation through disciplined risk management and allocations to DeFi protocols exhibiting adequate liquidity, solvency characteristics, and operational robustness, with the objective of limiting downside risk across market conditions.


# Investment framework

When managing assets, KPK strives to achieve the highest possible risk-adjusted returns. It follows an internal risk management framework that outlines a comprehensive set of risk parameters across all assets, positions, and protocols to which the portfolio is exposed.

The framework has three core components:

## **Initial Due Diligence**

A structured onchain and offchain due diligence review by KPK, complemented by signals from independent, external risk intelligence.\
Each asset, market, or strategy undergoes a structured review under the Due Diligence Framework. The assessment combines onchain analysis with targeted offchain validation to capture both technical and contextual risks.

## **Fundamental Risk Assessment**

After initial due diligence, fund managers apply a structured, layered evaluation framework to each protocol and collateral type considered for allocation decisions.

#### **Collateral Assessment**

Exposure to any collateral, regardless of the platform or protocol used, is central to the fundamental risk assessment. This analysis includes:

* Smart contract design and technological risk;
* Governance structure and stakeholder incentives;
* Collateral quality, including default and depeg risk;
* Liquidity mechanisms and historical behaviour;

#### Protocol Assessment

Focuses on the resilience of the protocol layer. The protocol is the application through which the Fund accesses a given market or strategy, with a specific collateral as the underlying asset. This assessment includes both on-chain and off-chain variables:

* Protocol architecture, including composability features, timelocks, and emergency procedures;
* Vault/Market design;
* Oracle mechanisms;
* Protocol Governance;
* External Audits and security reviews;

#### Blockchain Assessment

The Fund’s multi-chain approach provides an additional layer of flexibility, ultimately enhancing return opportunities. While this multi-chain approach is constrained to EVM-compatible chains, which promotes interoperability, tooling maturity, and developer standardisation, it does not eliminate underlying blockchain-level risks. Therefore, this layer of assessment includes:

* Network security and consensus mechanisms;
* Validator concentration;
* Historical uptime and incident record;
* Governance structure, including reliance on centralized infrastructure;
* Bridge design and cross-chain dependencies;

## **3. Allocation Decisions**

A formal rating system is applied to the collateral and protocol assessment layers. The outputs of these two assessments are combined into a composite rating for each collateral–protocol pairing. This composite rating directly determines the permissible allocation range and informs position sizing, leverage constraints, and exposure limits within the Fund.

By contrast, the Blockchain assessment operates in a whitelisting system, whereby a blockchain is either included in or excluded from the Fund’s eligible investment universe. No allocation may be made to protocols or positions deployed on non-approved chains.

### Additional Notes

Leverage may be employed where consistent with the prevailing market risk regime and the outcomes of the fundamental risk assessment. The Fund may also participate in private transactions within the approved protocol universe, subject to counterparties meeting internal risk standards.\
This layered evaluation framework is ultimately operationalised through a set of onchain Permission Policies, which enforce the Fund’s investment mandate and risk constraints in accordance with the Investment Framework.


# Policies and addresses

The Investment and Risk Management Frameworks are implemented as onchain [policies](/funds/infrastructure/core-concepts/policies) within the Fund’s dedicated investment vehicle. These policies enforce verifiable constraints on the addresses, functions and parameters the Fund can use, supporting automated execution, transparent operation and enforceable controls across all investment activity.

KPK’s fund infrastructure is non-custodial and deployed across multiple blockchains, comprising a network of Safes with distinct roles. The Portfolio Safe holds Fund assets and defines the permitted action set through onchain policy controls. Manager Safe [operators](/funds/infrastructure/core-concepts/roles-and-operators) execute transactions and workflows, ranging from asset allocation decisions to subscriptions and redemptions requests, with each transaction authorised and parameterised through a role-based onchain permission framework that enforces the Portfolio Safe’s constraints.

Use the fund dashboard’s **Policy** view for the full, up-to-date configuration. For how this Safe stack is assembled and why the addresses are identical across chains, see [Architecture overview](/funds/infrastructure/architecture-overview) and [Deployment](/funds/infrastructure/deployment).

### Addresses

#### Mainnet

<table><thead><tr><th width="173.48828125">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x99b9F5F24205Cb88E33b1CC72008f644Fc23768b</td></tr><tr><td>Manager Safe</td><td>0x933eAA3F9b237bE3F8bd88BeEee643e7ae72595d</td></tr><tr><td>NAV Calculator</td><td>0xa57A641417fe2703C5364C2f57f35297b16189a5</td></tr><tr><td>Shares Contract</td><td>0xED01a1Fe4e020Ca901F43E17B6203A7e0cEa818C</td></tr><tr><td>Roles Modifier</td><td>0x2bA2F894d0Ac9435346a40521Ae513D1be6d9B17</td></tr><tr><td>Sub-Roles Modifier</td><td>0x97Ab9e7a2275d4bD86913809b3c29C4dDA49c694</td></tr></tbody></table>

#### Arbitrum

<table><thead><tr><th width="173.48828125">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x99b9F5F24205Cb88E33b1CC72008f644Fc23768b</td></tr><tr><td>Manager Safe</td><td>0x933eAA3F9b237bE3F8bd88BeEee643e7ae72595d</td></tr><tr><td>NAV Calculator</td><td>0xa57A641417fe2703C5364C2f57f35297b16189a5</td></tr><tr><td>Roles Modifier</td><td>0x2bA2F894d0Ac9435346a40521Ae513D1be6d9B17</td></tr><tr><td>Sub-Roles Modifier</td><td>0x97Ab9e7a2275d4bD86913809b3c29C4dDA49c694</td></tr></tbody></table>

#### Base

<table><thead><tr><th width="173.48828125">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x99b9F5F24205Cb88E33b1CC72008f644Fc23768b</td></tr><tr><td>Manager Safe</td><td>0x933eAA3F9b237bE3F8bd88BeEee643e7ae72595d</td></tr><tr><td>NAV Calculator</td><td>0xa57A641417fe2703C5364C2f57f35297b16189a5</td></tr><tr><td>Roles Modifier</td><td>0x2bA2F894d0Ac9435346a40521Ae513D1be6d9B17</td></tr><tr><td>Sub-Roles Modifier</td><td>0x97Ab9e7a2275d4bD86913809b3c29C4dDA49c694</td></tr></tbody></table>

#### Optimism

<table><thead><tr><th width="173.48828125">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x99b9F5F24205Cb88E33b1CC72008f644Fc23768b</td></tr><tr><td>Manager Safe</td><td>0x933eAA3F9b237bE3F8bd88BeEee643e7ae72595d</td></tr><tr><td>NAV Calculator</td><td>0xa57A641417fe2703C5364C2f57f35297b16189a5</td></tr><tr><td>Roles Modifier</td><td>0x2bA2F894d0Ac9435346a40521Ae513D1be6d9B17</td></tr><tr><td>Sub-Roles Modifier</td><td>0x97Ab9e7a2275d4bD86913809b3c29C4dDA49c694</td></tr></tbody></table>

#### Gnosis

<table><thead><tr><th width="173.48828125">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Portfolio Safe</td><td>0x99b9F5F24205Cb88E33b1CC72008f644Fc23768b</td></tr><tr><td>Manager Safe</td><td>0x933eAA3F9b237bE3F8bd88BeEee643e7ae72595d</td></tr><tr><td>NAV Calculator</td><td>0xa57A641417fe2703C5364C2f57f35297b16189a5</td></tr><tr><td>Roles Modifier</td><td>0x2bA2F894d0Ac9435346a40521Ae513D1be6d9B17</td></tr><tr><td>Sub-Roles Modifier</td><td>0x97Ab9e7a2275d4bD86913809b3c29C4dDA49c694</td></tr></tbody></table>


# Architecture overview

How a fund is built: an onchain Safe stack with tokenized shares, priced by onchain NAV, complemented by an offchain indexing and automation layer.

Funds use a hybrid architecture. Custody, permissions, share issuance, and valuation live **onchain**; indexing, APIs, user interfaces, and automation run **offchain** on top of that onchain state.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-a660e1ad19fb19b1c0d8bea85a820ba70c64fa57%2Foiv-architecture.svg?alt=media" alt="A fund&#x27;s onchain contract stack: an Portfolio Safe holding assets behind three Zodiac Roles Modifiers, a Manager Safe for operators, and a kpkShares proxy for tokenized shares."><figcaption><p>The onchain contract stack of a single fund (Onchain Investment Vehicle).</p></figcaption></figure>

{% hint style="info" %}
Legacy terminology: older materials may refer to a fund as an **Onchain Investment Vehicle (OIV)**. It's the same underlying Funds infrastructure — the contracts repo is [`karpatkey/onchain-investment-vehicles`](https://github.com/karpatkey/onchain-investment-vehicles).
{% endhint %}

## Onchain components

Every fund is a stack of contracts deployed atomically by the [`KpkOivFactory`](/funds/infrastructure/deployment). A single fund spans one or more chains and is made up of:

* **Portfolio Safe** — the fund's vault. It holds all portfolio assets. Its **sole owner is the `Empty` contract** (`0xA470…4652`, deployed at the same address on every chain), so **no EOA or multisig can execute transactions on it directly**. Every action must flow through the Roles Modifiers.
* **Three Zodiac Roles Modifiers** — the programmable permission layer in front of the Safes:
  * **Exec Roles Modifier** — the primary execution layer, enabled as a module on the Portfolio Safe and owned by the fund **admin** (a Security Council / governance Safe). It is the authoritative gatekeeper of Portfolio Safe execution.
  * **Sub Roles Modifier** — a nested layer (avatar = Portfolio Safe, target = Exec Roles Modifier) used to route automated/bot transactions through the exec layer. Owned by the Manager Safe.
  * **Manager Roles Modifier** — guards the Manager Safe's own actions (avatar & target = Manager Safe). Owned by the Manager Safe.
* **Manager Safe** — the operators' multisig. Fund managers sign here; it holds the `OPERATOR` role on the shares contract and is the fund's fee receiver.
* **`kpkShares` contract** — an [ERC-20 token](/funds/infrastructure/core-concepts/shares) that is both the fund's shares **and** the onchain interface for [subscriptions](/funds/infrastructure/core-concepts/subscriptions) and [redemptions](/funds/infrastructure/core-concepts/redemptions). It is a **UUPS proxy** backed by an **implementation deployed exclusively for that fund**, so upgrades are isolated. The shares token lives on **one chain** (typically mainnet); on other chains a fund deploys only the Safe + modifier stack.
* **NAV Calculator** (one per chain) — computes the USD value of the Portfolio Safe's portfolio on that chain. It reads balances through balance adapters and prices them through price feeds. See [Onchain Accounting](/funds/infrastructure/onchain-accounting).

{% hint style="info" %}
The `kpkShares` contract does **not** compute NAV. NAV is calculated onchain by the NAV Calculator; the resulting **share price** is submitted by an operator when settling requests (see [NAV](/funds/infrastructure/core-concepts/nav)).
{% endhint %}

### Multichain layout

A fund's **Portfolio Safe, Manager Safe, and Roles Modifiers share the same address on every chain** — a deterministic CREATE2 property of the factory. The shares token is deployed on a single chain. Assets held across chains are valued by each chain's NAV Calculator and summed off-chain into one global NAV.

## Offchain components

The offchain infrastructure complements the onchain system and supports data aggregation, user interfaces, and automated operations:

* **Data indexer** — tracks shares events and NAV values, indexes them, and denormalises the data into multiple formats consumed by the API and the web application.
* **JSON API** — exposes current and historical fund data (NAV, supply, positions, policies, user positions) for the web application and external integrations.
* **Web application** — lets users review performance and portfolio breakdowns, inspect approved policies, and create subscription and redemption requests through a simple interface.
* **Automated agents** — approve subscription and redemption requests without manual operator intervention, and help keep positions safe (for example, exiting compromised protocols during emergencies). They act through the Sub Roles Modifier, within the bounds set by onchain [policies](/funds/infrastructure/core-concepts/policies).

## Where to go next

{% content-ref url="/pages/RlzHVX2QoIhmGagfv12b" %}
[Fund deployment](/funds/infrastructure/deployment)
{% endcontent-ref %}

{% content-ref url="/pages/hIrBCMheMit82G3MiTCW" %}
[Core concepts](/funds/infrastructure/core-concepts)
{% endcontent-ref %}


# Core concepts

The primitives that power shares, valuation, subscriptions, redemptions, fees, and permissions.

Core concepts define how funds work at the protocol level: how ownership is represented, how the fund is valued and priced, how users enter and exit, how fees are charged, and how operators can act on behalf of a fund without taking custody. These concepts apply across all funds, regardless of strategy or denomination. If you're integrating, these pages clarify what the onchain contracts guarantee — and what they do not.

In this section:

* [**Shares**](/funds/infrastructure/core-concepts/shares) — how the `kpkShares` ERC-20 token represents pro-rata ownership and how supply changes.
* [**NAV**](/funds/infrastructure/core-concepts/nav) — how the fund is valued onchain and how that valuation becomes a settlement share price.
* [**Subscriptions**](/funds/infrastructure/core-concepts/subscriptions) — the request-based deposit flow, from request to minted shares.
* [**Redemptions**](/funds/infrastructure/core-concepts/redemptions) — the request-based exit flow, from request to paid-out assets.
* [**Fees**](/funds/infrastructure/core-concepts/fees) — management, performance (high-water mark), and redemption fees.
* [**Roles and operators**](/funds/infrastructure/core-concepts/roles-and-operators) — how permissions are modelled and the Admin vs Operator APIs.
* [**Policies**](/funds/infrastructure/core-concepts/policies) — how onchain permissions constrain what a fund can do.
* [**Bridging**](/funds/infrastructure/core-concepts/bridging) — how cross-chain transfers affect accounting.


# Shares

The kpkShares ERC-20 token — pro-rata fund ownership with a request-based mint/burn model.

Shares represent **proportional ownership of the fund**. When users subscribe, new shares are minted to reflect their contribution. When they redeem, those shares are burned. At any point, a holder's share balance corresponds to their fraction of the fund's total Net Asset Value (NAV).

## The `kpkShares` contract

Each fund's shares are an instance of **`kpkShares`** — an **ERC-20 token** (18 decimals) that also serves as the **onchain interface** for [subscriptions](/funds/infrastructure/core-concepts/subscriptions) and [redemptions](/funds/infrastructure/core-concepts/redemptions). It is built on OpenZeppelin's upgradeable contracts:

* **`ERC20Upgradeable`** — standard token behaviour (`transfer`, `approve`, `balanceOf`, `totalSupply`, …).
* **`AccessControlUpgradeable`** — the `DEFAULT_ADMIN_ROLE` / `OPERATOR` permission model (see [Roles and operators](/funds/infrastructure/core-concepts/roles-and-operators)).
* **`UUPSUpgradeable`** — the contract is a proxy. Each fund gets its **own implementation** (deployed by the [factory](/funds/infrastructure/deployment)), so upgrading one fund never affects another.

### Request-based, not ERC-4626

While inspired by [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) tokenized vaults, `kpkShares` does **not** expose direct `deposit`/`withdraw`. Every inflow and outflow goes through an **operator-approved request**, an approach closer to [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) (asynchronous vaults). Concretely:

1. A user submits a request (`requestSubscription` / `requestRedemption`). Their assets — or shares — are moved into **escrow** inside the `kpkShares` contract while the request is `PENDING`.
2. An operator settles a batch of requests at a share price, minting or burning accordingly.

This lets the fund batch requests, control execution, and manage inflows and outflows from a risk and liquidity perspective. See [Subscriptions](/funds/infrastructure/core-concepts/subscriptions) and [Redemptions](/funds/infrastructure/core-concepts/redemptions) for the full lifecycle.

## Share price and decimals

The **price of a share** is the total NAV divided by the circulating supply. Share prices are expressed in **normalized USD units with 8 decimals**; the share token itself uses **18 decimals**. NAV is computed onchain by the [NAV Calculator](/funds/infrastructure/core-concepts/nav); the price used to settle a batch of requests is submitted by the operator and stored per asset as the *last settled price*.

The contract exposes pure conversion helpers (both take a `sharesPrice` in 8-decimal USD):

```solidity
// shares = (assetAmount * 10^18 * 1e8) / (sharesPrice * 10^assetDecimals)
function assetsToShares(uint256 assetAmount, uint256 sharesPrice, address subscriptionAsset)
    external view returns (uint256);

// assets = (shares * sharesPrice * 10^assetDecimals) / (1e8 * 10^18)
function sharesToAssets(uint256 shares, uint256 sharesPrice, address redemptionAsset)
    external view returns (uint256);
```

For computing minimums with the *last settled* price, prefer `previewSubscription` / `previewRedemption` — see [Calculate share price](/funds/integration/calculate-share-price).

## Approved assets

A fund can accept more than one asset. Each approved asset is described by:

```solidity
struct ApprovedAsset {
    address asset;            // ERC-20 token address
    string  symbol;           // e.g. "USDC"
    uint8   decimals;         // asset decimals (max 36)
    bool    isFeeModuleAsset; // counts toward performance-fee calculations
    bool    canDeposit;       // approved for subscriptions
    bool    canRedeem;        // approved for redemptions
}
```

The **base asset** set at deployment is automatically flagged `isFeeModuleAsset = true`. Operators manage the asset list with `updateAsset` (see [Roles and operators](/funds/infrastructure/core-concepts/roles-and-operators)); integrators read it with `getApprovedAssets` / `getApprovedAsset` / `isApprovedAsset` (see [List approved assets](/funds/integration/list-approved-assets)).


# NAV

How a fund is valued onchain, and how that valuation becomes the price used to settle requests.

The **Net Asset Value (NAV)** is the **total USD value of all assets held by the fund across every chain**. It is the basis for pricing shares and for the amounts exchanged during subscriptions and redemptions.

## NAV is computed onchain

Each chain has its own **NAV Calculator** contract, which:

* Reads the Portfolio Safe's portfolio balances directly from token contracts or via **balance adapters** (for positions not held as plain ERC-20 tokens).
* Prices those balances using **price feeds** (Chainlink, Redstone, API3, or custom composite feeds).
* Produces a chain-local NAV, **normalized to 8 decimals**.

An offchain worker aggregates the per-chain NAV values into a single global NAV. This hybrid model combines transparent onchain valuation with consistent multichain accounting. For the full contract reference, see [Onchain Accounting](/funds/infrastructure/onchain-accounting).

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2FcZjERaBJJIQ2ov5jhKoG%2FNAV.png?alt=media&amp;token=595d2b41-4188-4ef4-8bfb-3c66938a089c" alt=""><figcaption></figcaption></figure>

## From NAV to a settlement price

The `kpkShares` contract does **not** read NAV itself. Instead, when an operator settles a batch of requests, they pass a **share price** — the global NAV divided by circulating supply — into `processRequests(...)`. That price is denominated in **normalized USD units (8 decimals)** and is used to convert between assets and shares for every request in the batch.

After a successful settlement, the contract stores the price as the **last settled price** for that asset:

```solidity
// last settled price per share, in 8-decimal USD; 0 if never settled
function getLastSettledPrice(address asset) external view returns (uint256);
```

The last settled price has two uses:

* It backs `previewSubscription` / `previewRedemption` when callers pass `sharesPrice = 0`, so integrators can estimate amounts without sourcing a live price (reverts `NoStoredPrice` if none exists yet).
* It anchors the **price-deviation guard** below.

## Price-deviation guard

To contain operator error or manipulation, each settlement price is checked against the last settled price for that asset:

```
deviationBps = abs(newPrice - lastPrice) * 10_000 / lastPrice
```

If the deviation exceeds **3000 bps (30%)**, `processRequests` reverts with `PriceDeviationTooLarge`. The first settlement for an asset has no prior price and is therefore not checked.

{% hint style="warning" %}
A NAV reading should only be used for subscriptions/redemptions when it is **complete** — for example, when no cross-chain transfer is mid-flight. See [Bridging](/funds/infrastructure/core-concepts/bridging) and [Calculate share price](/funds/integration/calculate-share-price).
{% endhint %}


# Subscriptions

The request-based deposit flow — from an investor's request to operator-settled, newly minted shares.

A subscription is a **user's request to enter the fund** by depositing an approved asset (for example USDC) in exchange for **newly minted shares** priced against the current NAV.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-dea20bbaf230c03a83b99343dbe54e2fbc77fe34%2Fsubscription-flow.svg?alt=media" alt="Subscription flow: an investor requests a subscription, assets are escrowed, the operator settles at a price, and shares are minted while assets move to the Portfolio Safe."><figcaption><p>The subscription lifecycle.</p></figcaption></figure>

## Lifecycle

{% stepper %}
{% step %}
**Request.** After approving the asset, the investor calls `requestSubscription(assetsIn, minSharesOut, subscriptionAsset, receiver)`. The deposited assets are pulled into **escrow** inside the `kpkShares` contract, a `PENDING` request is recorded, and a `SubscriptionRequest` event is emitted with the request id. `minSharesOut` is **slippage protection** — the actual shares are computed at settlement time. `receiver` may differ from the investor.
{% endstep %}

{% step %}
**Settlement.** An operator calls `processRequests(approveIds, rejectIds, asset, sharesPrice)`. For each approved subscription it computes `shares = assetsToShares(assetAmount, sharesPrice, asset)`, checks `shares ≥ minSharesOut`, **mints** the shares to the `receiver`, and transfers the escrowed assets to the **Portfolio Safe** (portfolio). The request becomes `PROCESSED` and `SubscriptionApproval` is emitted.
{% endstep %}

{% step %}
**Or rejection / expiry.** An operator can reject a request (`SubscriptionDenial`), returning the escrowed assets to the investor. Any request older than its 7-day lifetime is skipped during processing and auto-rejected (`SubscriptionRequestExpired`), also returning the assets.
{% endstep %}

{% step %}
**Or cancellation.** While still `PENDING`, the investor or receiver can cancel after the request's TTL has elapsed (`cancelSubscription(id)`), recovering the escrowed assets. See [Cancel a request](/funds/integration/cancel-a-request).
{% endstep %}
{% endstepper %}

A request moves through exactly one terminal state: `PROCESSED`, `REJECTED`, or `CANCELLED`.

## The request event

```solidity
event SubscriptionRequest(
    address indexed investor,
    uint256 requestId,
    address indexed receiver,
    address indexed subscriptionAsset,
    uint256 assetsAmount,
    uint256 sharesAmount,   // the minSharesOut at request time
    uint64  timestamp,
    uint64  cancelableFrom, // timestamp + subscriptionRequestTtl
    uint64  expiryAt        // timestamp + 7 days
);
```

`cancelableFrom` is when the investor may cancel; `expiryAt` is when the request can no longer be approved. See [Subscription request](/funds/integration/subscription-request) for integration code, and [Fees](/funds/infrastructure/core-concepts/fees) for the management/performance fees charged during settlement.


# Redemptions

The request-based exit flow — from a share-holder's request to operator-settled, paid-out assets.

A redemption is a **user's request to exit the fund** by returning shares in exchange for one of the approved payout assets (for example USDC), priced against the current NAV.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-1fbcd65d5e9d19f8dd2a05e9969cafa901438ce3%2Fredemption-flow.svg?alt=media" alt="Redemption flow: a holder requests a redemption, shares are escrowed, the operator settles at a price, a redemption fee is taken, the rest is burned, and assets are paid from the Portfolio Safe."><figcaption><p>The redemption lifecycle.</p></figcaption></figure>

## Lifecycle

{% stepper %}
{% step %}
**Request.** The holder calls `requestRedemption(sharesIn, minAssetsOut, redemptionAsset, receiver)`. The shares are pulled into **escrow** inside the `kpkShares` contract, a `PENDING` request is recorded, and a `RedemptionRequest` event is emitted. `minAssetsOut` is **slippage protection**, applied to the amount **after** the redemption fee. The `redemptionAsset` must be approved for redemptions (`canRedeem = true`), otherwise the call reverts with `UnredeemableAsset`.
{% endstep %}

{% step %}
**Settlement.** An operator calls `processRequests(approveIds, rejectIds, asset, sharesPrice)`. For each approved redemption it takes the **redemption fee** in shares (`fee = sharesIn * redemptionFeeRate / 10_000`) and transfers it to the fee receiver, computes `assetsOut = sharesToAssets(sharesIn - fee, sharesPrice, asset)`, checks `assetsOut ≥ minAssetsOut`, **burns** the net shares from escrow, and transfers the assets from the **Portfolio Safe** to the `receiver`. The request becomes `PROCESSED` and `RedemptionApproval` is emitted (including the `redemptionFee`).
{% endstep %}

{% step %}
**Or rejection / expiry / cancellation.** As with subscriptions, an operator can reject a request (`RedemptionDenial`), an expired request is auto-rejected (`RedemptionRequestExpired`), and the holder or receiver can cancel while `PENDING` once the TTL has elapsed (`cancelRedemption(id)`). In every case the escrowed shares are returned.
{% endstep %}
{% endstepper %}

## The request event

```solidity
event RedemptionRequest(
    address indexed investor,
    uint256 requestId,
    address indexed receiver,
    address indexed redemptionAsset,
    uint256 assetsAmount,   // the minAssetsOut at request time
    uint256 sharesAmount,   // shares being redeemed
    uint64  timestamp,
    uint64  cancelableFrom, // timestamp + redemptionRequestTtl
    uint64  expiryAt        // timestamp + 7 days
);
```

This mechanism lets users exit at transparent, NAV-aligned terms while the fund manages liquidity and the pacing of outflows. See [Redemption request](/funds/integration/redemption-request) for integration code and [Fees](/funds/infrastructure/core-concepts/fees) for how the redemption fee fits alongside management and performance fees.


# Fees

The three fees a fund can charge — management, performance (high-water mark), and redemption.

A fund can charge three fees, all configured by the [admin](/funds/infrastructure/core-concepts/roles-and-operators) and all paid as **newly minted (or transferred) shares** to the fund's **fee receiver** (the Manager Safe). Every rate is expressed in **basis points** and capped at **2000 bps (20%)**.

| Fee             | When it is charged                                            | Base                         |
| --------------- | ------------------------------------------------------------- | ---------------------------- |
| **Management**  | During request settlement, time-based                         | Net share supply, annualized |
| **Performance** | During request settlement, on gains above the high-water mark | Net share supply             |
| **Redemption**  | On each approved redemption                                   | The shares being redeemed    |

Throughout, **net supply** means `totalSupply - feeReceiverBalance` — the fee receiver's own shares are excluded so fees don't compound on themselves.

## Management fee

Accrues continuously and is realized when `processRequests` runs, provided at least **`MIN_TIME_ELAPSED` (6 hours)** have passed since the last management-fee update:

```
managementFee = netSupply * managementFeeRate * timeElapsed / (10_000 * 365 days)
```

The elapsed time is tracked with a single timestamp shared across all assets, so the fee is charged once per settlement window regardless of which asset is being processed.

## Performance fee (high-water mark)

Computed by a pluggable module — `WatermarkFee` — and only charged when the share price rises **above the highest price previously seen** (the *high-water mark*). It is evaluated during settlement when the processed asset has `isFeeModuleAsset = true` and a module is configured.

```solidity
function calculatePerformanceFee(
    uint256 sharesPrice,  // current price, 8-decimal USD
    uint256 timeElapsed,
    uint256 feePct,       // performanceFeeRate, bps
    uint256 netSupply
) external returns (uint256 fee);
```

Logic:

* If `sharesPrice ≤ highWatermark`, the fee is **0** — no profit above the mark.
* Otherwise the watermark is raised to the current price and the fee is taken on the profit since the previous mark:

```
profitPerShare = highWatermark - previousWatermark
totalProfit    = profitPerShare * netSupply / highWatermark
performanceFee = totalProfit * feePct / 10_000
```

Because the mark only ever moves up, a draw-down must be fully recovered before any new performance fee is charged. Swapping the module (or disabling it with `address(0)`) is an admin action; the base asset is flagged `isFeeModuleAsset = true` at deployment so performance fees can be computed for it.

## Redemption fee

Taken in shares on every approved redemption, before the remaining shares are burned:

```
redemptionFee = sharesRedeemed * redemptionFeeRate / 10_000
```

The fee shares go to the fee receiver; the net shares are burned and the corresponding assets are paid to the receiver. See [Redemptions](/funds/infrastructure/core-concepts/redemptions).

## Events

When management and/or performance fees are charged, the contract emits a single event (only if at least one is non-zero):

```solidity
event FeeCollection(uint256 managementFee, uint256 performanceFee);
```

The redemption fee is reported in the `RedemptionApproval` event's `redemptionFee` field. Rate changes emit `ManagementFeeRateUpdate` / `PerformanceFeeRateUpdate` / `RedemptionFeeRateUpdate` **only when the value actually changes**, and any fee accrued under the old rate is charged before the new rate takes effect.


# Roles and operators

The Admin and Operator roles on the shares contract, and the Safe + Roles Modifier stack behind them.

Although funds are permissionless to enter and exit, certain operations are restricted. There are two distinct permission systems:

* **On the `kpkShares` contract** — OpenZeppelin `AccessControl` roles (`DEFAULT_ADMIN_ROLE`, `OPERATOR`) gate fund configuration and request settlement.
* **On the Portfolio Safe** — Zodiac **Roles Modifiers** gate what the fund can do with its assets (see [Policies](/funds/infrastructure/core-concepts/policies)).

## Roles on the shares contract

#### Admin

* OpenZeppelin's default role; held by a Security Council / governance Safe.
* Manages contract-level configuration (fees, TTLs, fee receiver, performance-fee module) and authorizes UUPS upgrades.
* Role key: `0x00` (`DEFAULT_ADMIN_ROLE`).

#### Operator

* Held by the fund's **Manager Safe**.
* Settles subscription and redemption requests and manages the list of accepted assets.
* Role key: `keccak256("OPERATOR")` = `0x523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c`.

### Permission matrix

| Function                                                                  | Admin | Operator | Public |
| ------------------------------------------------------------------------- | :---: | :------: | :----: |
| `requestSubscription` / `requestRedemption`                               |       |          |    ✅   |
| `cancelSubscription` / `cancelRedemption` (investor/receiver, after TTL)  |       |          |    ✅   |
| `recoverAssets`                                                           |       |          |    ✅   |
| `processRequests`                                                         |       |     ✅    |        |
| `updateAsset`                                                             |       |     ✅    |        |
| `setManagementFeeRate` / `setRedemptionFeeRate` / `setPerformanceFeeRate` |   ✅   |          |        |
| `setPerformanceFeeModule` / `setFeeReceiver`                              |   ✅   |          |        |
| `setSubscriptionRequestTtl` / `setRedemptionRequestTtl`                   |   ✅   |          |        |
| UUPS upgrade (`_authorizeUpgrade`)                                        |   ✅   |          |        |

## Admin API

Configuration calls, all restricted to `DEFAULT_ADMIN_ROLE`. Fee setters that change a value charge any fee accrued under the old rate first; setters emit their update event **only when the value changes**.

```solidity
function setManagementFeeRate(uint256 newRate) external;             // bps, ≤ 2000
function setRedemptionFeeRate(uint256 newRate) external;             // bps, ≤ 2000
function setPerformanceFeeRate(uint256 newRate, address usdAsset) external; // bps, ≤ 2000
function setPerformanceFeeModule(address newPerformanceFeeModule) external; // address(0) disables
function setFeeReceiver(address newFeeReceiver) external;
function setSubscriptionRequestTtl(uint64 ttl) external;             // ≤ 7 days
function setRedemptionRequestTtl(uint64 ttl) external;               // ≤ 7 days
```

A TTL change applies to **all** pending requests, not just new ones — it shifts when existing requests become cancellable.

## Operator API

Settlement and asset management, restricted to the `OPERATOR` role (the Manager Safe).

```solidity
// Settle a batch for one asset at a given share price (8-decimal USD).
// Charges fees, validates the price-deviation guard, then mints/burns per request.
function processRequests(
    uint256[] calldata approveRequests,
    uint256[] calldata rejectRequests,
    address asset,
    uint256 sharesPrice
) external;

// Add, reconfigure, or remove an approved asset.
function updateAsset(address asset, bool isFeeModuleAsset, bool canDeposit, bool canRedeem) external;
```

`updateAsset` enforces safety rules: an asset cannot be removed while it has pending subscriptions or requests, the last approved asset cannot be removed, an asset cannot be added with both `canDeposit` and `canRedeem` false, and asset decimals are capped at 36.

## Operators in practice

Operators are KPK contributors responsible for the operational management of funds. Much of the day-to-day work is handled by **automated agents** routed through the Sub Roles Modifier; operators remain responsible for actions that cannot be executed autonomously, including:

* Approving or rejecting pending subscription and redemption requests.
* Deploying capital into pre-approved DeFi strategies, as defined by the fund's [policies](/funds/infrastructure/core-concepts/policies).
* Bridging assets between the fund's Safes across chains (see [Bridging](/funds/infrastructure/core-concepts/bridging)).
* Managing liquidity and maintaining cash positions to meet redemptions.

## The Safe + Roles Modifier stack

Operators never hold the Portfolio Safe's keys. Execution is mediated by three Zodiac Roles Modifiers deployed with every fund (see [Deployment](/funds/infrastructure/deployment)):

* **Exec Roles Modifier** — the primary layer in front of the Portfolio Safe; owned by the **admin**. The authoritative gatekeeper of all Portfolio Safe execution.
* **Sub Roles Modifier** — nested inside the exec layer; routes automated/bot transactions through it. Owned by the Manager Safe.
* **Manager Roles Modifier** — guards the Manager Safe's own actions. Owned by the Manager Safe.

The Portfolio Safe's only owner is the `Empty` contract, so there is no key that can bypass this stack.


# Policies

Onchain permissions that constrain what a fund can do with its assets.

Policies define the set of onchain permissions that govern how a fund may operate. They are enforced through the [Zodiac Roles Modifier](https://github.com/gnosisguild/zodiac-modifier-roles), which acts as a programmable permission layer in front of each fund's Portfolio Safe.

Because the Portfolio Safe's only owner is the `Empty` contract, **no key can move the fund's assets directly** — every transaction must pass through the **Exec Roles Modifier** (and, for automated flows, the nested **Sub Roles Modifier**). See [Roles and operators](/funds/infrastructure/core-concepts/roles-and-operators) for the full stack.

Policies specify which actions can be executed on behalf of the fund — for example interacting with a specific DeFi protocol, managing a liquidity position, or initiating an approved bridging transaction — down to the target address, function selector, and parameter level. Any operation that falls outside the authorized policies is **blocked at the contract level**.

This framework lets day-to-day portfolio management run smoothly while keeping the fund strictly confined to its risk profile and predefined execution rules. Each fund's current configuration is available in the dashboard's **Policy** view and summarized on its **Policies and addresses** page.


# Bridging

How cross-chain transfers of fund assets affect NAV.

Bridging refers to the movement of a fund's assets between its Safes on different chains, letting operators rebalance liquidity or deploy capital across networks. Because a fund's **Portfolio Safe has the same address on every chain** (a deterministic property of the [factory](/funds/infrastructure/deployment)), bridged assets stay within the fund's own custody on both sides.

While a bridge transaction is in progress, the assets are **in transit** — they have left the source chain but have not yet arrived on the destination chain, so during that window they are not reflected in either chain's balances. The global NAV (the sum of per-chain NAVs) therefore temporarily **under-counts** by the in-flight amount until the transfer settles.

{% hint style="warning" %}
A global NAV taken while a bridge is mid-flight is incomplete and is **not** a true representation of the fund's value; it must not be used to settle subscriptions or redemptions until the transfer completes on the destination chain. See [Calculate share price](/funds/integration/calculate-share-price).
{% endhint %}


# Fund deployment

How funds are created: the KpkOivFactory deploys the whole Safe + shares stack in one transaction, with deterministic addresses across chains.

Funds are not deployed by hand. A single onchain factory, **`KpkOivFactory`**, assembles the entire contract stack — Safes, Roles Modifiers, and the shares token — in one transaction, fully wired and with ownership already transferred to the right parties.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-2762b01cedf1e7577b5f3c6e534a664d3e4283a9%2Ffactory-deployment.svg?alt=media" alt="KpkOivFactory deploying the seven-contract fund stack in a single transaction."><figcaption><p>A single <code>deployOiv</code> call produces all seven contracts of a fund.</p></figcaption></figure>

## The factory

`KpkOivFactory` is already deployed at the **same address on every supported chain**. It has two permissionless entry points; only its infrastructure setters are restricted to the factory owner.

| Entry point                | Deploys                                                                                                                   | Used for                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `deployStack(StackConfig)` | The **5-contract operational stack**: Portfolio Safe, Manager Safe, and the three Roles Modifiers.                        | Sidechains — extending an existing fund to another chain.   |
| `deployOiv(OivConfig)`     | The **same 5-contract stack plus** a per-fund `kpkShares` implementation and its ERC-1967 UUPS proxy (7 contracts total). | The chain where the shares token lives (typically mainnet). |

### What `deployOiv` produces

{% stepper %}
{% step %}
**Portfolio Safe** — holds fund assets; sole owner is the `Empty` contract, so it can only be driven through the Roles Modifiers.
{% endstep %}

{% step %}
**Manager Safe** — operators' multisig; granted the `OPERATOR` role on the shares token.
{% endstep %}

{% step %}
**Exec / Sub / Manager Roles Modifiers** — the permission layer (see [Roles and operators](/funds/infrastructure/core-concepts/roles-and-operators)). The exec modifier is owned by `admin`; the sub and manager modifiers are owned by the Manager Safe.
{% endstep %}

{% step %}
**`kpkShares` implementation** — deployed fresh for this fund so its upgrade surface is isolated.
{% endstep %}

{% step %}
**`kpkShares` proxy** — the fund's ERC-20 shares token. Investors hold this. The factory initializes it, registers any additional assets, grants the Portfolio Safe → proxy allowances needed for redemptions, wires the Manager Safe as `OPERATOR`, hands `DEFAULT_ADMIN_ROLE` to `admin`, and renounces its own roles.
{% endstep %}
{% endstepper %}

The deploying account **retains no privileged role** afterward — all authority sits with `admin` and the Manager Safe configured at deploy time.

## Deterministic, cross-chain addresses

A single `salt` drives every CREATE2 deployment, with the caller's address mixed in to prevent salt-squatting. The consequence is the **cross-chain invariant**:

> For the same `(caller, salt)` on the same factory, `deployStack` and `deployOiv` produce **identical** Portfolio Safe, Manager Safe, and Roles Modifier addresses on **every** chain.

That is what lets a fund run `deployOiv` on mainnet and `deployStack` on each sidechain while keeping a **single Portfolio Safe address everywhere**. Two view functions return the addresses a deployment would produce, without sending a transaction:

* `predictStackAddresses(StackConfig, caller)` → the 5 stack addresses.
* `predictOivAddresses(OivConfig, caller)` → all 7 addresses.

{% hint style="info" %}
The same deployer account must be used across all chains to keep addresses identical.
{% endhint %}

Running `deployStack` on each sidechain by hand is optional: the **`CcipOivDeployer`** orchestrator does the whole fan-out from a **single mainnet transaction** over Chainlink CCIP, keeping the same addresses everywhere. See [Cross-chain deployment (CCIP)](/funds/infrastructure/deployment/cross-chain-deployment).

## Configuration

`deployOiv` takes an `OivConfig`. The shares parameters mirror the fund's economic settings (see [Fees](/funds/infrastructure/core-concepts/fees) and [Subscriptions](/funds/infrastructure/core-concepts/subscriptions)); fee rates are in **basis points** (100 bps = 1%), TTLs in **seconds**.

```solidity
struct OivConfig {
    SafeConfig managerSafe;              // operator multisig: owners + threshold
    uint256 salt;                        // drives all deterministic addresses
    address admin;                       // exec-modifier owner + DEFAULT_ADMIN_ROLE on shares
    KpkShares.ConstructorParams sharesParams;  // name, symbol, base asset, fees, TTLs, fee receiver
    AssetConfig[] additionalAssets;      // extra deposit/redemption assets beyond the base asset
}

struct KpkShares.ConstructorParams {
    address asset;                  // base asset (e.g. USDC) — auto-flagged isFeeModuleAsset = true
    address admin;                  // ignored; overridden by OivConfig.admin
    string  name;                   // share token name
    string  symbol;                 // share token symbol
    address safe;                   // ignored; overridden with the deployed Portfolio Safe
    uint64  subscriptionRequestTtl; // ≤ 7 days
    uint64  redemptionRequestTtl;   // ≤ 7 days
    address feeReceiver;            // receives management/performance/redemption fee shares
    uint256 managementFeeRate;      // bps, ≤ 2000
    uint256 redemptionFeeRate;      // bps, ≤ 2000
    address performanceFeeModule;   // WatermarkFee module, or address(0) to disable
    uint256 performanceFeeRate;     // bps, ≤ 2000
}
```

The factory validates the config before deploying (non-empty/duplicate-free Manager Safe owners, valid threshold, non-zero `admin` and base asset, no duplicate additional assets, required shares params set).

## Addresses

`KpkOivFactory`, `KpkSharesDeployer`, the `CcipOivDeployer` orchestrator, and the `Empty` contract are deployed at the **same address on every supported chain** (19 chains):

| Contract                       | Address                                      |
| ------------------------------ | -------------------------------------------- |
| `KpkOivFactory`                | `0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F` |
| `KpkSharesDeployer`            | `0xea084E763F8535CBe28759b990F963BeDf60be9a` |
| `CcipOivDeployer`              | `0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b` |
| `Empty` (Portfolio Safe owner) | `0xA4703438f8cc4fc2C2503a7e43935Da16BA74652` |

The full list of chains and ownership is on the [Deployment addresses](/funds/infrastructure/deployment/deployment-addresses) page. Per-fund addresses are listed on each fund's **Policies and addresses** page (for example, [USD Alpha](/funds/funds/usd-alpha/policies-and-addresses)).

{% hint style="warning" %}
**Trust assumptions.** The factory **owner** controls all infrastructure setters (Safe singleton, Zodiac mastercopy, shares deployer, …) **with no timelock**, so it must be a governance multisig or `TimelockController` — never an EOA. For a given fund, the **Manager Safe owners** receive ownership of the sub and manager Roles Modifiers, so they must be trusted at the same operational level as `admin`; the exec Roles Modifier (owned by `admin`) remains the authoritative gatekeeper of Portfolio Safe execution.
{% endhint %}


# Cross-chain deployment (CCIP)

How a single mainnet transaction deploys a fund on mainnet and fans the operational stack out to sidechains over Chainlink CCIP — same addresses everywhere.

A fund spans one or more chains, and its Portfolio Safe, Manager Safe, and Roles Modifiers must share the **same address on every chain** (see [Architecture overview](/funds/infrastructure/architecture-overview)). Doing that by hand means running [`deployStack`](/funds/infrastructure/deployment) on each sidechain from the same account, in separate transactions. **`CcipOivDeployer`** automates it: a **single mainnet transaction** deploys the full OIV on mainnet **and** fans the operational stack out to every configured sidechain over **Chainlink CCIP** — producing identical Portfolio / Manager / Roles addresses across all chains.

**Source:** [`CcipOivDeployer.sol`](https://github.com/karpatkey/onchain-investment-vehicles/blob/main/src/CcipOivDeployer.sol)

| At a glance      |                                                                               |
| ---------------- | ----------------------------------------------------------------------------- |
| **Contract**     | `CcipOivDeployer` (external orchestrator around `KpkOivFactory`)              |
| **Address**      | `0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b` — same on every chain            |
| **Source chain** | Ethereum mainnet (`SOURCE_CHAIN_ID = 1`)                                      |
| **Entry points** | `deployEverywhere` · `dispatchTo` (permissionless, caller-funded)             |
| **Addresses**    | [Deployment addresses](/funds/infrastructure/deployment/deployment-addresses) |

***

## Why an orchestrator (not CCIP inside the factory)

`KpkOivFactory` mixes `msg.sender` into every CREATE2 salt to stop salt-squatting, so its cross-chain address invariant holds only when the **same caller** invokes the factory on every chain. A raw CCIP integration would break this — on a destination chain the factory's caller would be the CCIP Router, not the original mainnet account.

`CcipOivDeployer` solves it by being the **single, uniform caller** of the factory on every chain. Because the orchestrator is itself deployed at the **same address on all chains** (deterministic CREATE2, identical creation code), the factory sees one identical `msg.sender` everywhere, and the address invariant is preserved — with **no CCIP logic inside the factory's deployment path**. All CCIP, fee, and router logic lives in the orchestrator.

***

## The flow

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-785916fa6f5c2e552aadd6902246404ace0d87b7%2Fccip-cross-chain.svg?alt=media" alt="A single mainnet deployEverywhere call: CcipOivDeployer deploys the full OIV on mainnet and, via the CCIP router and network, delivers to the sibling orchestrator on each sidechain, which deploys the operational stack at the same addresses."><figcaption><p>One mainnet <code>deployEverywhere</code> call deploys the full OIV on mainnet and fans the operational stack out to each sidechain over CCIP — same addresses everywhere.</p></figcaption></figure>

The mainnet leg deploys the **full OIV** (the 5-contract operational stack **plus** the `kpkShares` token) via `deployOiv`. Each sidechain leg deploys the **operational stack only** via `deployStack`, at the same addresses — the shares token lives on one chain, while every chain holds a matching Portfolio Safe for bridged portfolio assets. The orchestrator derives each sidechain's `StackConfig` by calling the factory's own `oivToStackConfig(config)` helper at runtime, so the mapping can never drift and fragment a fund's addresses.

***

## Entry points

Both write entry points are **permissionless** and **caller-funded** (fees paid in native gas as `msg.value` — see [CCIP fees](#ccip-fees)), and run only on the source chain (mainnet), reverting `NotSourceChain` elsewhere.

| Function                                                    | Purpose                                                                                                              |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `deployEverywhere(config, gasLimit)`                        | Deploy the full OIV on mainnet and fan out to **all configured** destination chains.                                 |
| `deployEverywhere(config, destChainIds[], gasLimit)`        | Same, but only to the given destination chains.                                                                      |
| `dispatchTo(config, destChainIds[], gasLimit)`              | CCIP fan-out **only** (no local `deployOiv`) — to add a chain later or retry a permanently-failed delivery.          |
| `quoteDeployEverywhere(config, [destChainIds[],] gasLimit)` | View: total native fee and per-destination breakdown to size `msg.value`.                                            |
| `predictOiv(config)`                                        | View: the addresses a deployment of `config` would produce (applies the orchestrator's salt derivation — see below). |

```solidity
function deployEverywhere(KpkOivFactory.OivConfig calldata config, uint256 gasLimit)
    external payable returns (KpkOivFactory.OivInstance memory instance, bytes32[] memory messageIds);

function deployEverywhere(KpkOivFactory.OivConfig calldata config, uint256[] calldata destChainIds, uint256 gasLimit)
    external payable returns (KpkOivFactory.OivInstance memory instance, bytes32[] memory messageIds);

function dispatchTo(KpkOivFactory.OivConfig calldata config, uint256[] calldata destChainIds, uint256 gasLimit)
    external payable returns (bytes32[] memory messageIds);

function quoteDeployEverywhere(KpkOivFactory.OivConfig calldata config, uint256 gasLimit)
    external view returns (uint256 totalFee, uint256[] memory feePerDestination);

function predictOiv(KpkOivFactory.OivConfig calldata config)
    external view returns (KpkOivFactory.OivInstance memory);
```

{% hint style="info" %}
**Config-bound salt.** The orchestrator is the factory's *uniform* caller, which would neutralise the factory's caller-mixed anti-squat salt. To restore it, the orchestrator derives the salt from the **whole config** — `salt = keccak256(abi.encode(config))`. Any config difference (notably `admin`) changes **every** deployed address, so an attacker cannot land a fund at another config's addresses; an identical config still yields identical addresses on every chain. Off-chain tooling must predict via **`predictOiv(config)`**, not the factory's raw `predictOivAddresses`.
{% endhint %}

***

## CCIP fees

Every cross-chain message costs a CCIP fee. `deployEverywhere` sends **one message per destination chain**, so the amount to pay is the **sum of the per-destination fees**.

* **Paid in native gas, by the caller.** The fee is paid in the **source chain's native token** (ETH on mainnet) as the **`msg.value`** of the `deployEverywhere` / `dispatchTo` call — the account that triggers the deployment funds it. CCIP is **not** paid in LINK here, and the orchestrator holds **no balance**, so there is no shared treasury to pre-fund or drain.
* **What a fee covers.** For each destination, the CCIP Router prices delivering the message **and** executing `ccipReceive` there up to the `gasLimit` passed. The total therefore scales with the **number of destinations** and the **gas limit**.
* **Quote before you send.** `quoteDeployEverywhere(config, [destChainIds,] gasLimit)` is a view that returns the **`totalFee`** and the **per-destination breakdown**. Send at least `totalFee` as `msg.value`.
* **Surplus is refunded.** If `msg.value` exceeds the total fee, the excess is **refunded to the caller** in the same transaction — so sending a small buffer over the quote is safe.
* **The gas limit is prepaid in the fee.** `gasLimit` is the destination execution budget baked into each message's price (`deployStack` measures \~1.45M gas — pass \~1.8M–2.0M; CCIP caps destination execution at **3M**). It is **prepaid**, so **unspent destination gas is not refunded**, and too low a limit makes the destination delivery fail (see [Operational model](#operational-model)).

{% hint style="info" %}
No LINK pre-funding or fee treasury is required. The `CcipDeployEverywhere` deploy script calls `quoteDeployEverywhere` and forwards the result as `msg.value` automatically, with a small buffer.
{% endhint %}

***

## Security model

`ccipReceive` accepts a message only when **all three** hold:

1. `msg.sender` is the configured CCIP Router.
2. `message.sourceChainSelector` is the configured Ethereum-mainnet selector.
3. The decoded source sender equals `address(this)` — the sibling orchestrator on mainnet (same address everywhere).

Check (3) blocks a forged message from pre-occupying a salt's deterministic CREATE2 addresses and griefing the legitimate deployment. `deployEverywhere` / `dispatchTo` are restricted to the source chain so a permissionless caller cannot run the full `deployOiv` directly on a *destination* chain and pre-occupy the stack addresses (which would make the later CCIP `deployStack` collide and stick in `FAILED`).

{% hint style="info" %}
The orchestrator never holds a privileged role on any deployed fund. The exec Roles Modifier — owned by `config.admin` — remains the authoritative gatekeeper of Portfolio Safe execution ([Roles and operators](/funds/infrastructure/core-concepts/roles-and-operators)).
{% endhint %}

***

## Operational model

{% hint style="warning" %}
Cross-chain delivery is **asynchronous, not atomic.** The mainnet transaction confirms once the messages are dispatched; each sidechain stack materialises later (after Ethereum finality, \~15 min) when CCIP delivers to `ccipReceive`.
{% endhint %}

* **Partial failure is possible.** A destination message can fail (e.g. gas underestimate, or a missing `Empty` contract on that chain). It then enters CCIP's `FAILED` state and can be **manually re-executed** within its retry window. Monitor delivery on the [CCIP Explorer](https://ccip.chain.link).
* **Recovery / add-a-chain.** `deployEverywhere` is the first, atomic fan-out and cannot be re-run with the same config (its local `deployOiv` would collide on CREATE2 addresses). To extend a fund to a new sidechain — or resend to one whose delivery permanently failed — use **`dispatchTo`** with the **same** `config` (notably the same `salt`), so the stack lands at the fund's existing addresses. Never re-dispatch to a chain that already has the stack.
* **Fees.** Paid by the caller in native gas as `msg.value`; quote and size them per [CCIP fees](#ccip-fees).
* **Gas limit.** Too low a `gasLimit` makes the destination `deployStack` run out of gas and the message enter `FAILED` (re-executable) — size it as in [CCIP fees](#ccip-fees).
* **`Empty` precondition.** `deployStack` reverts `EmptyContractMissing` unless the [`Empty` contract](/funds/infrastructure/deployment/deployment-addresses) is predeployed on the target chain — ensure this first.
* **New funds only.** Addresses are keyed to the orchestrator, so a fund previously deployed directly by an EOA cannot be retro-extended through this path; a fund using CCIP must enter through the orchestrator from the start.

***

## Supported destinations

The mainnet orchestrator's selector registry covers every deployed sidechain (each chain except mainnet itself). The orchestrator, factory, and `Empty` contract are live on the same chains — see [Deployment addresses](/funds/infrastructure/deployment/deployment-addresses) for the full list.


# Deployment addresses

Deployment addresses for the OIV factory infrastructure — the canonical CREATE2 layer (factory, shares deployer, orchestrator) shared across every chain.

The **OIV factory infrastructure** — the contracts that deploy funds (see [Fund deployment](/funds/infrastructure/deployment) and [Cross-chain deployment](/funds/infrastructure/deployment/cross-chain-deployment)) — is deployed at the **same address on every supported chain**. All four contracts are placed by the canonical CREATE2 deployer (`0x4e59b44847b379578588920cA78FbF26c0B4956C`) with identical bytecode and constructor arguments, so their addresses are deterministic and chain-independent.

{% hint style="success" %}
**The factory:** `KpkOivFactory` at [`0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F`](https://etherscan.io/address/0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F) — the same address on every chain. Funds are deployed by calling it (directly, or via the CCIP orchestrator).
{% endhint %}

The machine source of truth is the contracts repo: [`script/deployed-infra.json`](https://github.com/karpatkey/onchain-investment-vehicles/blob/main/script/deployed-infra.json) (per-chain deploy block, tx hash, and verification), mirrored by [`docs/DEPLOYED_ADDRESSES.md`](https://github.com/karpatkey/onchain-investment-vehicles/blob/main/docs/DEPLOYED_ADDRESSES.md).

***

## Canonical layer (CREATE2 — identical on every chain)

This is the current infrastructure. The four contracts share one address on every chain.

| Contract            | Address                                                                                                                 | Role                                                                                                                                                                   |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `KpkOivFactory`     | [`0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F`](https://etherscan.io/address/0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F) | Deploys a fund's Safe + Roles + shares stack in one transaction ([Fund deployment](/funds/infrastructure/deployment)).                                                 |
| `KpkSharesDeployer` | [`0xea084E763F8535CBe28759b990F963BeDf60be9a`](https://etherscan.io/address/0xea084E763F8535CBe28759b990F963BeDf60be9a) | Deploys each fund's dedicated `kpkShares` implementation (called by the factory).                                                                                      |
| `CcipOivDeployer`   | [`0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b`](https://etherscan.io/address/0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b) | Cross-chain orchestrator — one mainnet tx fans a fund out to sidechains over CCIP ([Cross-chain deployment](/funds/infrastructure/deployment/cross-chain-deployment)). |
| `Empty`             | [`0xA4703438f8cc4fc2C2503a7e43935Da16BA74652`](https://etherscan.io/address/0xA4703438f8cc4fc2C2503a7e43935Da16BA74652) | The Portfolio Safe's sole signer — a contract with no logic, so the Safe can only act through its Roles Modifiers.                                                     |

***

## Ownership

| Party                   | Address                                                                                                                 | After deploy                                                                                                                                                                  |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **OIV governance Safe** | [`0x8b884f80B3B839F52b6cE168f133e7a5D1f0A537`](https://etherscan.io/address/0x8b884f80B3B839F52b6cE168f133e7a5D1f0A537) | `Ownable.owner` of the factory and the orchestrator on every chain (same address everywhere). Controls the infrastructure setters.                                            |
| Deployer EOA            | [`0xAa5A7C7Ea51F276301f881F9CCB501a1dFeF4F72`](https://etherscan.io/address/0xAa5A7C7Ea51F276301f881F9CCB501a1dFeF4F72) | Seeded configuration (e.g. the orchestrator's mainnet selector registry) **before** handover, then transferred ownership to the Safe. Holds **no** privileged role afterward. |

{% hint style="warning" %}
The factory **owner** controls all infrastructure setters (Safe singleton, Zodiac mastercopy, shares deployer, CCIP config) **with no timelock**, so it must remain a governance multisig — never an EOA. See the trust assumptions on [Fund deployment](/funds/infrastructure/deployment).
{% endhint %}

***

## Chains

The infrastructure is deployed and source-verified on **19 chains** — on each, the factory and orchestrator are owned by the OIV governance Safe: Ethereum (1), Optimism (10), Gnosis (100), Base (8453), Arbitrum (42161), BSC (56), Polygon (137), Avalanche (43114), Celo (42220), Linea (59144), Scroll (534352), Sonic (146), Unichain (130), World Chain (480), HyperEVM (999), Mantle (5000), Plasma (9745), Ink (57073), Berachain (80094).

{% hint style="info" %}
These are the shared **factory** addresses. A fund's own contracts (Portfolio Safe, Manager Safe, Roles Modifiers, `kpkShares`) are per-fund and listed on each fund's **Policies and addresses** page — for example, [USD Alpha](/funds/funds/usd-alpha/policies-and-addresses).
{% endhint %}


# Code examples

Look up, predict, and deploy KPK funds through the KpkOivFactory and the CCIP orchestrator, with viem, ethers.js, and Python.

Practical examples for looking up a fund's contract addresses, predicting them ahead of a deployment, and deploying a fund across chains — through the [`KpkOivFactory`](/funds/infrastructure/deployment) and the [`CcipOivDeployer`](/funds/infrastructure/deployment/cross-chain-deployment) orchestrator.

**Source:** [`KpkOivFactory.sol`](https://github.com/karpatkey/onchain-investment-vehicles/blob/main/src/KpkOivFactory.sol) · [`CcipOivDeployer.sol`](https://github.com/karpatkey/onchain-investment-vehicles/blob/main/src/CcipOivDeployer.sol)

{% hint style="info" %}
The factory and orchestrator share the **same address on every chain** — take them from [Deployment addresses](/funds/infrastructure/deployment/deployment-addresses). The addresses below are placeholders.
{% endhint %}

{% hint style="info" %}
In the returned `OivInstance`, the field named **`avatarSafe`** is the fund's **Portfolio Safe** (the Zodiac module's *avatar* — the Safe that holds the fund's assets); **`kpkSharesProxy`** is the ERC-20 shares token investors hold.
{% endhint %}

***

## 1. Look up a deployed fund

Funds registered on the factory are returned by `getFund(registeredFundId)` as an `OivInstance` — the seven per-fund addresses.

```solidity
struct OivInstance {
    address avatarSafe;           // the fund's Portfolio Safe (holds assets)
    address managerSafe;
    address execRolesModifier;
    address subRolesModifier;
    address managerRolesModifier;
    address kpkSharesImpl;
    address kpkSharesProxy;        // the ERC-20 shares token
}

function getFund(uint256 registeredFundId) external view returns (OivInstance memory);
```

{% tabs %}
{% tab title="viem" %}

```typescript
import { createPublicClient, http, parseAbi } from "viem";
import { mainnet } from "viem/chains";

const FACTORY = "0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F"; // same on every chain

const ABI = parseAbi([
  `function getFund(uint256 registeredFundId) view returns (
     (address avatarSafe, address managerSafe, address execRolesModifier,
      address subRolesModifier, address managerRolesModifier,
      address kpkSharesImpl, address kpkSharesProxy))`,
]);

const client = createPublicClient({ chain: mainnet, transport: http() });

const fund = await client.readContract({
  address: FACTORY, abi: ABI, functionName: "getFund", args: [0n],
});
console.log("Portfolio Safe:", fund.avatarSafe);
console.log("Shares token:  ", fund.kpkSharesProxy);
```

{% endtab %}

{% tab title="ethers.js" %}

```typescript
import { ethers } from "ethers";

const FACTORY = "0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F";

const ABI = [
  `function getFund(uint256 registeredFundId) view returns (
     tuple(address avatarSafe, address managerSafe, address execRolesModifier,
           address subRolesModifier, address managerRolesModifier,
           address kpkSharesImpl, address kpkSharesProxy))`,
];

const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
const factory = new ethers.Contract(FACTORY, ABI, provider);

const fund = await factory.getFund(0);
console.log("Portfolio Safe:", fund.avatarSafe);
console.log("Shares token:  ", fund.kpkSharesProxy);
```

{% endtab %}

{% tab title="Python" %}

```python
from web3 import Web3

FACTORY = "0xbafbca1804B6e46D4c54Cac0A0273F5B2A8F677F"

OIV_INSTANCE = [
    {"name": "avatarSafe",           "type": "address"},
    {"name": "managerSafe",          "type": "address"},
    {"name": "execRolesModifier",    "type": "address"},
    {"name": "subRolesModifier",     "type": "address"},
    {"name": "managerRolesModifier", "type": "address"},
    {"name": "kpkSharesImpl",        "type": "address"},
    {"name": "kpkSharesProxy",       "type": "address"},
]
ABI = [{
    "name": "getFund", "type": "function", "stateMutability": "view",
    "inputs": [{"name": "registeredFundId", "type": "uint256"}],
    "outputs": [{"name": "", "type": "tuple", "components": OIV_INSTANCE}],
}]

w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
fund = w3.eth.contract(address=FACTORY, abi=ABI).functions.getFund(0).call()
print("Portfolio Safe:", fund[0])   # avatarSafe
print("Shares token:  ", fund[6])   # kpkSharesProxy
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
`predictOivAddresses(config, caller)` on the factory returns the same `OivInstance` for a set of inputs **without** sending a transaction — handy to look up a fund's Portfolio Safe before it is deployed. For CCIP-deployed funds, predict through the orchestrator instead (next section), which applies the config-bound salt.
{% endhint %}

***

## 2. Deploy a fund across chains

A single mainnet call to `CcipOivDeployer.deployEverywhere` deploys the full OIV on mainnet and fans the operational stack out to every configured sidechain (see [Cross-chain deployment](/funds/infrastructure/deployment/cross-chain-deployment)). Build an `OivConfig`, **predict** the resulting addresses, **quote** the native CCIP fee, then **deploy** with that fee as `msg.value`.

```solidity
function predictOiv(OivConfig config) external view returns (OivInstance);
function quoteDeployEverywhere(OivConfig config, uint256 gasLimit)
    external view returns (uint256 totalFee, uint256[] memory feePerDestination);
function deployEverywhere(OivConfig config, uint256 gasLimit)
    external payable returns (OivInstance instance, bytes32[] memory messageIds);
```

{% hint style="warning" %}
`deployEverywhere` must run on **Ethereum mainnet** (the source chain) and is **payable** — send at least `totalFee` from `quoteDeployEverywhere` as `msg.value`; any surplus is refunded. The **config-bound salt** means every field (notably `admin`) affects every deployed address, so predict with the exact config you will deploy.
{% endhint %}

{% tabs %}
{% tab title="viem" %}

```typescript
import { createPublicClient, createWalletClient, http, parseAbi, zeroAddress } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

const CCIP = "0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b"; // orchestrator, same on every chain
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

// viem's parseAbi understands struct definitions — declare them once, reuse below.
const ABI = parseAbi([
  "struct SafeConfig { address[] owners; uint256 threshold; }",
  "struct AssetConfig { address asset; bool canDeposit; bool canRedeem; }",
  "struct ConstructorParams { address asset; address admin; string name; string symbol; address safe; uint64 subscriptionRequestTtl; uint64 redemptionRequestTtl; address feeReceiver; uint256 managementFeeRate; uint256 redemptionFeeRate; address performanceFeeModule; uint256 performanceFeeRate; }",
  "struct OivConfig { SafeConfig managerSafe; uint256 salt; address admin; ConstructorParams sharesParams; AssetConfig[] additionalAssets; }",
  "struct OivInstance { address avatarSafe; address managerSafe; address execRolesModifier; address subRolesModifier; address managerRolesModifier; address kpkSharesImpl; address kpkSharesProxy; }",
  "function predictOiv(OivConfig config) view returns (OivInstance)",
  "function quoteDeployEverywhere(OivConfig config, uint256 gasLimit) view returns (uint256 totalFee, uint256[] feePerDestination)",
  "function deployEverywhere(OivConfig config, uint256 gasLimit) payable returns (OivInstance instance, bytes32[] messageIds)",
]);

const MANAGER_SAFE_OWNERS = ["0x1111...", "0x2222...", "0x3333..."];
const ADMIN = "0xAdmiSafe..."; // exec-modifier owner + DEFAULT_ADMIN_ROLE on shares

const config = {
  managerSafe: { owners: MANAGER_SAFE_OWNERS, threshold: 2n },
  salt: 1n,                              // any uint256; same inputs → same addresses
  admin: ADMIN,
  sharesParams: {
    asset: USDC,                         // base asset
    admin: zeroAddress,                  // ignored (overridden by config.admin)
    name: "KPK USD Alpha",
    symbol: "kUSD",
    safe: zeroAddress,                   // ignored (set to the deployed Portfolio Safe)
    subscriptionRequestTtl: 86400n,      // ≤ 7 days
    redemptionRequestTtl: 86400n,        // ≤ 7 days
    feeReceiver: "0xFeeReceiver...",
    managementFeeRate: 100n,             // 1.00% (bps, ≤ 2000)
    redemptionFeeRate: 0n,
    performanceFeeModule: zeroAddress,   // disabled
    performanceFeeRate: 0n,
  },
  additionalAssets: [],                  // e.g. [{ asset: DAI, canDeposit: true, canRedeem: true }]
} as const;

const GAS_LIMIT = 1_800_000n;            // per-destination deployStack budget (~1.45M measured)

const client = createPublicClient({ chain: mainnet, transport: http() });

// 1) predict the deterministic addresses
const predicted = await client.readContract({
  address: CCIP, abi: ABI, functionName: "predictOiv", args: [config],
});
console.log("Portfolio Safe will be:", predicted.avatarSafe);

// 2) quote the native CCIP fee
const [totalFee] = await client.readContract({
  address: CCIP, abi: ABI, functionName: "quoteDeployEverywhere", args: [config, GAS_LIMIT],
});

// 3) deploy (mainnet only), paying the fee as msg.value
const wallet = createWalletClient({
  account: privateKeyToAccount("0x<PRIVATE_KEY>"), chain: mainnet, transport: http(),
});
const hash = await wallet.writeContract({
  address: CCIP, abi: ABI, functionName: "deployEverywhere", args: [config, GAS_LIMIT],
  value: totalFee,
});
console.log("deployEverywhere tx:", hash);
```

{% endtab %}

{% tab title="ethers.js" %}

```typescript
import { ethers } from "ethers";

const CCIP = "0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

// Reused tuple shapes (ethers has no struct keyword — inline tuples).
const OIV_CONFIG =
  "tuple(tuple(address[] owners, uint256 threshold) managerSafe, uint256 salt, address admin, " +
  "tuple(address asset, address admin, string name, string symbol, address safe, " +
  "uint64 subscriptionRequestTtl, uint64 redemptionRequestTtl, address feeReceiver, " +
  "uint256 managementFeeRate, uint256 redemptionFeeRate, address performanceFeeModule, " +
  "uint256 performanceFeeRate) sharesParams, tuple(address asset, bool canDeposit, bool canRedeem)[] additionalAssets)";
const OIV_INSTANCE =
  "tuple(address avatarSafe, address managerSafe, address execRolesModifier, address subRolesModifier, " +
  "address managerRolesModifier, address kpkSharesImpl, address kpkSharesProxy)";

const ABI = [
  `function predictOiv(${OIV_CONFIG} config) view returns (${OIV_INSTANCE})`,
  `function quoteDeployEverywhere(${OIV_CONFIG} config, uint256 gasLimit) view returns (uint256 totalFee, uint256[] feePerDestination)`,
  `function deployEverywhere(${OIV_CONFIG} config, uint256 gasLimit) payable returns (${OIV_INSTANCE} instance, bytes32[] messageIds)`,
];

const config = {
  managerSafe: { owners: ["0x1111...", "0x2222...", "0x3333..."], threshold: 2 },
  salt: 1,
  admin: "0xAdminSafe...",
  sharesParams: {
    asset: USDC,
    admin: ethers.ZeroAddress,           // ignored
    name: "KPK USD Alpha",
    symbol: "kUSD",
    safe: ethers.ZeroAddress,            // ignored
    subscriptionRequestTtl: 86400,
    redemptionRequestTtl: 86400,
    feeReceiver: "0xFeeReceiver...",
    managementFeeRate: 100,              // 1.00%
    redemptionFeeRate: 0,
    performanceFeeModule: ethers.ZeroAddress,
    performanceFeeRate: 0,
  },
  additionalAssets: [],
};
const GAS_LIMIT = 1_800_000n;

const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
const orchestrator = new ethers.Contract(CCIP, ABI, provider);

const predicted = await orchestrator.predictOiv(config);
console.log("Portfolio Safe will be:", predicted.avatarSafe);

const [totalFee] = await orchestrator.quoteDeployEverywhere(config, GAS_LIMIT);

const signer = new ethers.Wallet("0x<PRIVATE_KEY>", provider);
const tx = await orchestrator.connect(signer).deployEverywhere(config, GAS_LIMIT, { value: totalFee });
console.log("deployEverywhere tx:", tx.hash);
```

{% endtab %}

{% tab title="Python" %}

```python
from web3 import Web3

CCIP = "0x6F2A3D35Ff275d6B76dB47eFB0Da1b2358daf11b"
USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"

SAFE_CONFIG = [{"name": "owners", "type": "address[]"}, {"name": "threshold", "type": "uint256"}]
ASSET_CONFIG = [{"name": "asset", "type": "address"}, {"name": "canDeposit", "type": "bool"}, {"name": "canRedeem", "type": "bool"}]
PARAMS = [
    {"name": "asset", "type": "address"}, {"name": "admin", "type": "address"},
    {"name": "name", "type": "string"}, {"name": "symbol", "type": "string"}, {"name": "safe", "type": "address"},
    {"name": "subscriptionRequestTtl", "type": "uint64"}, {"name": "redemptionRequestTtl", "type": "uint64"},
    {"name": "feeReceiver", "type": "address"}, {"name": "managementFeeRate", "type": "uint256"},
    {"name": "redemptionFeeRate", "type": "uint256"}, {"name": "performanceFeeModule", "type": "address"},
    {"name": "performanceFeeRate", "type": "uint256"},
]
OIV_CONFIG = {"name": "config", "type": "tuple", "components": [
    {"name": "managerSafe", "type": "tuple", "components": SAFE_CONFIG},
    {"name": "salt", "type": "uint256"}, {"name": "admin", "type": "address"},
    {"name": "sharesParams", "type": "tuple", "components": PARAMS},
    {"name": "additionalAssets", "type": "tuple[]", "components": ASSET_CONFIG},
]}
OIV_INSTANCE = [{"name": n, "type": "address"} for n in
    ["avatarSafe", "managerSafe", "execRolesModifier", "subRolesModifier",
     "managerRolesModifier", "kpkSharesImpl", "kpkSharesProxy"]]

ABI = [
    {"name": "predictOiv", "type": "function", "stateMutability": "view",
     "inputs": [OIV_CONFIG], "outputs": [{"name": "", "type": "tuple", "components": OIV_INSTANCE}]},
    {"name": "quoteDeployEverywhere", "type": "function", "stateMutability": "view",
     "inputs": [OIV_CONFIG, {"name": "gasLimit", "type": "uint256"}],
     "outputs": [{"name": "totalFee", "type": "uint256"}, {"name": "feePerDestination", "type": "uint256[]"}]},
    {"name": "deployEverywhere", "type": "function", "stateMutability": "payable",
     "inputs": [OIV_CONFIG, {"name": "gasLimit", "type": "uint256"}],
     "outputs": [{"name": "instance", "type": "tuple", "components": OIV_INSTANCE},
                 {"name": "messageIds", "type": "bytes32[]"}]},
]

config = (
    (["0x1111...", "0x2222...", "0x3333..."], 2),   # managerSafe (owners, threshold)
    1,                                              # salt
    "0xAdminSafe...",                               # admin
    (USDC, "0x" + "0" * 40, "KPK USD Alpha", "kUSD", "0x" + "0" * 40,
     86400, 86400, "0xFeeReceiver...", 100, 0, "0x" + "0" * 40, 0),  # sharesParams
    [],                                             # additionalAssets
)
GAS_LIMIT = 1_800_000

w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
orchestrator = w3.eth.contract(address=CCIP, abi=ABI)

predicted = orchestrator.functions.predictOiv(config).call()
print("Portfolio Safe will be:", predicted[0])   # avatarSafe

total_fee, _ = orchestrator.functions.quoteDeployEverywhere(config, GAS_LIMIT).call()

# Deploy (mainnet only): build, sign, and send with value = total_fee
acct = w3.eth.account.from_key("0x<PRIVATE_KEY>")
tx = orchestrator.functions.deployEverywhere(config, GAS_LIMIT).build_transaction({
    "from": acct.address, "value": total_fee, "nonce": w3.eth.get_transaction_count(acct.address),
})
signed = acct.sign_transaction(tx)
print("deployEverywhere tx:", w3.eth.send_raw_transaction(signed.raw_transaction).hex())
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Deploy to a **subset** of chains with the `deployEverywhere(config, destChainIds[], gasLimit)` overload, and add a chain to an existing fund later — or retry a permanently-failed CCIP delivery — with `dispatchTo(config, destChainIds[], gasLimit)` using the **same** `config`. See [Cross-chain deployment](/funds/infrastructure/deployment/cross-chain-deployment#operational-model).
{% endhint %}


# Onchain Accounting

Verifiable, manipulation-resistant NAV for each fund — composed from onchain balances and prices in one contract per chain.

KPK's onchain accounting system produces a verifiable, manipulation-resistant **Net Asset Value (NAV)** for each fund by composing three layers of onchain infrastructure into a single contract — the **NAV Calculator** — deployed once per chain.

{% hint style="info" %}
Each fund's NAV is computed independently on every chain where it operates, then aggregated offchain into a single global figure. Every computation input — balances and prices — is read from onchain sources and is independently verifiable.
{% endhint %}

### How it fits together

The NAV Calculator inherits two libraries of behaviour — **Balances** and **Prices** — and combines them into the NAV layer.

{% stepper %}
{% step %}

#### Balance layer

For each tracked asset, the NAV Calculator collects the account's holdings from a set of registered **balance adapters**. A built-in `ERC20DefaultBalanceAdapter` reports plain wallet balances; protocol adapters report positions that are not visible as a simple `balanceOf` (Morpho supply shares, Aave aTokens, staked LP, reward accruals, …). Each adapter returns balance-only **`PositionBalance`** entries tagged with a machine-readable identity (the `protocolBrand`/`protocolId`/`protocolSubId` taxonomy, `positionId`, `positionKind`, `isLocked`).
{% endstep %}

{% step %}

#### Price layer

Each balance is priced by the asset's single **primary price feed** (Chainlink, Redstone, API3, or a custom composite adapter). Any additional trustworthy source is a **monitor feed**, read only to check the primary for divergence — never to price NAV. If the primary is stale, the asset is added to `NAV.stalePriceAssets` and the reading is flagged unreliable; if the primary diverges from a monitor (or, for stablecoins, strays from $1) beyond tolerance, the asset is added to `NAV.irregularPriceAssets` as a soft alert. If monitors are configured but **none of them was readable**, the divergence check could not run at all, and the asset is added to `NAV.monitorsUnhealthyPriceAssets` — so a clean `irregular` flag is never mistaken for a check that actually happened.
{% endstep %}

{% step %}

#### Aggregation layer

On each chain, the NAV Calculator prices every position and sums them into a **chain-local NAV** (`int256`, in the quote asset's decimals — 8 for USD). Debt positions subtract. The offchain **NAV Worker** collects per-chain values and sums them into the **global NAV** used for subscriptions and redemptions.
{% endstep %}
{% endstepper %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-a593f68dbefeabf795bb2339dd4599b297ad241f%2Fnav-architecture.svg?alt=media" alt="Flowchart: getAccountNav gathers balance-adapter positions, prices each, aggregates a per-chain NAV, and an off-chain worker sums healthy chains into a global NAV."><figcaption><p>NAV computation pipeline: balance adapters report positions, prices value them, the per-chain NAV is aggregated, and the off-chain worker sums chains into the global NAV.</p></figcaption></figure>

### Architecture

* A single **`NAVCalculator`** is deployed per chain as a UUPS proxy at a canonical CREATE2 address (identical across chains). It is **fund-agnostic** — one instance prices any account on that chain.
* It inherits the `Prices` and `Balances` registries, and delegates heavy registry mutations and read loops to external libraries (`NAVRegistryLib`, `PriceFeedLib`) to stay under the EIP-170 contract-size limit.
* Human-readable labels come from the NAV Calculator's own **verbose read methods** (`getAccountNavVerbose` / `getAccountPositionsVerbose`) — the label-enrichment path is inlined into the contract rather than split into a separate lens. There is no standalone lens contract.
* Chains where the funds hold positions: **Ethereum, Optimism, Arbitrum, Base, Gnosis** — the NAV is also live (canonical layer only) on 19 more chains. See [Deployment addresses](/funds/infrastructure/onchain-accounting/deployment-addresses).
* **License:** the contracts are released under **BUSL-1.1** (licensor KPK; converts to GPL-2.0-or-later on 2030-07-01), with a GPL-2.0-or-later exception for the bundled Uniswap V3 math libraries.

### Key contracts

| Component       | Contract                             | Purpose                                                                                                                                |
| --------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| NAV Calculator  | `NAVCalculator` (UUPS proxy)         | Registers assets, feeds, and adapters; prices balances; returns chain NAV and positions, plus labelled `*Verbose` reads for dashboards |
| Default adapter | `ERC20DefaultBalanceAdapter`         | Reports plain wallet balances for every registered ERC-20 + the native token                                                           |
| Balance adapter | Various (plain & meta)               | Report protocol positions as balance-only `PositionBalance` entries                                                                    |
| Price feed      | Chainlink / Redstone / API3 / custom | Return a price and staleness signal per asset                                                                                          |

### Explore the section

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Contracts</strong></td><td>The NAV Calculator — the contract you read from.</td><td><a href="/funds/infrastructure/onchain-accounting/contracts">Contracts</a></td></tr><tr><td><strong>Concepts</strong></td><td>Position identity and the stale-price / sequencer model.</td><td><a href="/funds/infrastructure/onchain-accounting/concepts">Concepts</a></td></tr><tr><td><strong>Price feeds</strong></td><td>The custom feeds and how prices are resolved.</td><td><a href="/funds/infrastructure/onchain-accounting/price-feeds">Price feeds</a></td></tr><tr><td><strong>Balance adapters</strong></td><td>The default + singleton adapters that locate positions.</td><td><a href="/funds/infrastructure/onchain-accounting/balance-adapters">Balance adapters</a></td></tr><tr><td><strong>Meta balance adapters</strong></td><td>One adapter per protocol, serving many instances.</td><td><a href="/funds/infrastructure/onchain-accounting/meta-balance-adapters">Meta balance adapters</a></td></tr><tr><td><strong>Assisted balance adapters</strong></td><td>Permissionless, owner-fed adapters for positions not discoverable on-chain.</td><td><a href="/funds/infrastructure/onchain-accounting/assisted-balance-adapters">Assisted balance adapters</a></td></tr><tr><td><strong>Attested balance adapters</strong></td><td>Proof-verified adapters for positions the chain can verify but not value.</td><td><a href="/funds/infrastructure/onchain-accounting/attested-balance-adapters">Attested balance adapters</a></td></tr><tr><td><strong>Deployment addresses</strong></td><td>Per-chain contract addresses for each chain.</td><td><a href="/funds/infrastructure/onchain-accounting/deployment-addresses">Deployment addresses</a></td></tr><tr><td><strong>Code examples</strong></td><td>Read NAV, positions, and prices in TypeScript / Python.</td><td><a href="/funds/infrastructure/onchain-accounting/code-examples">Code examples</a></td></tr></tbody></table>


# Contracts

The contract integrators read from: the NAV Calculator.

The single onchain contract an integrator reads from is the **NAV Calculator** — the core accounting contract, one upgradeable instance per chain. It both prices balances and, through its **verbose read methods** (`getAccountNavVerbose` / `getAccountPositionsVerbose`), returns positions enriched with human-readable labels for dashboards. There is no separate lens contract; the verbose path is inlined into the NAV Calculator. Deployed addresses are on the [Deployment addresses](/funds/infrastructure/onchain-accounting/deployment-addresses) page.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>NAV Calculator</strong></td><td>Registers assets, feeds, and adapters; prices balances; returns chain NAV and positions (plus labelled <code>*Verbose</code> reads for dashboards). UUPS proxy.</td><td><a href="/funds/infrastructure/onchain-accounting/contracts/nav-calculator">NAV Calculator</a></td></tr></tbody></table>


# NAV Calculator

The core accounting contract — registers assets, feeds, and adapters, and returns NAV and positions.

The **NAV Calculator** (`NAVCalculator`) is the core accounting contract. It registers the assets a fund can hold, the price feeds that value them, and the balance adapters that locate them, then returns the fund's NAV and full position breakdown for any account on that chain.

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

| At a glance   |                                                                                       |
| ------------- | ------------------------------------------------------------------------------------- |
| **Contract**  | `NAVCalculator`                                                                       |
| **Type**      | UUPS upgradeable proxy — one instance per chain                                       |
| **Roles**     | `DEFAULT_ADMIN_ROLE` · `MANAGER` (security council Safe)                              |
| **Chains**    | Ethereum · Optimism · Arbitrum · Base · Gnosis · BSC · Polygon (+ 17 canonical-only)  |
| **Addresses** | [Deployment addresses](/funds/infrastructure/onchain-accounting/deployment-addresses) |

***

It is **fund-agnostic** — a single instance per chain prices any account. It is deployed as a UUPS proxy at a canonical CREATE2 address, identical across all chains, and is upgradeable by the security council Safe.

{% hint style="info" %}
**Roles are held by the karpatkey security council Safe** `0x8b884f80B3B839F52b6cE168f133e7a5D1f0A537` — both `DEFAULT_ADMIN_ROLE` (proxy upgrades, sequencer-feed setters) and `MANAGER` (asset, feed and adapter configuration), on every chain.
{% endhint %}

{% hint style="info" %}
Deployed addresses are listed on the [Deployment addresses](/funds/infrastructure/onchain-accounting/deployment-addresses) page. Always call the **proxy**; the implementation may change on upgrade.
{% endhint %}

### How it works

```
getAccountNav(account, quoteAsset)
  ├─ for each registered balance adapter:
  │    └─ adapter.getAdapterPositions(account, …) → PositionBalance[]
  ├─ for each position:
  │    ├─ (price, decimals, stale) = primary feed for balanceAsset
  │    ├─ irregular = primary vs monitor feeds beyond tolerance (signal only)
  │    └─ value = ±(amount × price) / 10^(assetDecimals)          // − when isDebt
  └─ return NAV {
         value,               // total in quote asset units (USD = 8 decimals)
         quoteAsset,
         timestamp,
         stalePriceAssets,    // assets whose primary feed was stale
         irregularPriceAssets,// assets whose primary diverged from monitors / peg
         sequencerDown,       // L2 sequencer status
         quoteAssetStale,     // quote-asset feed stale → value falls back to USD
         monitorsUnhealthyPriceAssets  // monitors configured, none readable → no divergence check ran
     }
```

### Solidity interface

#### Structs

The interface defines the position model, the NAV snapshot, and the price-feed snapshot. Expand each group for the full definitions.

<details>

<summary><strong>Position model — <code>Asset</code>, <code>PositionBalance</code>, <code>Position</code>, <code>VerbosePosition</code>, <code>VerboseNAV</code></strong></summary>

```solidity
struct Asset {
    address asset;
    string  symbol;
    uint8   decimals;
}

/// Balance-only data returned by adapters (no pricing).
struct PositionBalance {
    Asset        balanceAsset;       // token the amount is denominated in
    address      balanceAdapter;     // adapter that reported it (provenance, not identity)
    uint256      amount;             // magnitude (always positive); sign implied by isDebt
    bool         isDebt;             // true = subtract from NAV
    bytes32      protocolBrand;      // keccak256 of the version-stripped brand slug (e.g. "aave"); grouping only
    bytes32      protocolId;         // keccak256 of the ecosystem slug (e.g. "aave-v3"); roll-up only
    bytes32      protocolSubId;      // keccak256 of the product slug (e.g. "aave-v3-lending"); IN the identity key, selects the positionId schema
    bytes        positionId;         // ABI-encoded static identifier; decode schema set by protocolSubId
    PositionKind positionKind;       // Wallet / Supplied / Borrowed / Collateral / Staking / Rewards / Fees
    bool         isLocked;           // true = not withdrawable now (pending/cooling); mutable per-leg attribute, NOT in the identity key
    bytes32      positionInstanceId; // ephemeral per-item handle (NFT id, withdrawal-request id, exit ticket); bytes32(0) if none; NOT in the identity key
}

/// Position enriched with pricing.
struct Position {
    Asset        balanceAsset;
    address      balanceAdapter;
    uint256      amount;
    bool         isDebt;
    Asset        quoteAsset;
    int256       value;              // value in quote asset (negative for debt)
    int256       price;
    uint8        priceDecimals;
    bool         stale;
    bool         quoteAssetStale;     // true if the quote-asset feed was stale; value then falls back to USD (8 dp)
    bytes32      protocolBrand;
    bytes32      protocolId;
    bytes32      protocolSubId;
    bytes        positionId;
    PositionKind positionKind;
    bool         isLocked;            // mutable per-leg attribute, NOT in the identity key
    bytes32      positionInstanceId;  // ephemeral per-item handle; NOT in the identity key
    bool         irregular;           // primary price diverged from monitors / peg (signal only)
    bool         quoteAssetIrregular; // quote-asset primary diverged from its monitors (false for USD quote)
}

/// Position plus its protocol name and labels (returned by the verbose read methods).
struct VerbosePosition {
    Position position;
    string   protocolName;         // e.g. "Aave V3", "Morpho" — from the adapter's IPositionDescribable.protocolName()
    string[] labels;               // display breadcrumb below the protocol name — from the adapter's positionLabels(positionId); full breadcrumb = [protocolName, ...labels]
}

/// NAV snapshot plus its verbose positions.
struct VerboseNAV {
    NAV               nav;
    VerbosePosition[] positions;
}
```

</details>

<details>

<summary><strong>NAV snapshot — <code>NAV</code></strong></summary>

```solidity
struct NAV {
    int256  value;                 // total in quote asset units (can be negative if net debt)
    Asset   quoteAsset;            // quote currency; address(0) = USD
    uint64  timestamp;             // block timestamp of the reading
    Asset[] stalePriceAssets;      // assets whose primary feed was stale
    bool    sequencerDown;         // true if the L2 sequencer is down / in grace period (L2 only)
    bool    quoteAssetStale;       // true if the quote-asset feed was stale; value then falls back to USD
    Asset[] irregularPriceAssets;  // assets whose primary diverged from monitors / peg (signal only)
    bool    quoteAssetIrregular;   // quote-asset primary diverged from its monitors (false for USD quote)
    Asset[] monitorsUnhealthyPriceAssets;  // monitors configured but none readable → divergence check did not run
}
```

New fields are **appended**, never inserted mid-struct, so an in-place UUPS upgrade cannot shift an existing field's ABI offset. `monitorsUnhealthyPriceAssets` is the most recent append.

</details>

<details>

<summary><strong>Partial NAV snapshot — <code>PartialNAV</code></strong></summary>

Returned by `getAccountNavForAdapters`, for paging a NAV read that no longer fits in a single `eth_call`. It mirrors `NAV`'s first eight fields **in order**, then appends the adapters the slice actually covered:

```solidity
struct PartialNAV {
    int256  value;                 // this slice's contribution only
    Asset   quoteAsset;
    uint64  timestamp;
    Asset[] stalePriceAssets;      // only assets THIS slice touched
    bool    sequencerDown;         // per slice
    bool    quoteAssetStale;       // per slice
    Asset[] irregularPriceAssets;  // only assets THIS slice touched
    bool    quoteAssetIrregular;   // per slice
    address[] adaptersCovered;
    Asset[] monitorsUnhealthyPriceAssets;  // only assets THIS slice touched
}
```

**The positional mirror stops at those eight fields.** `monitorsUnhealthyPriceAssets` sits *after* `adaptersCovered` here, while in `NAV` it is the last field — so the two structs no longer line up past the prefix. That is deliberate: keeping `adaptersCovered` at the offset a live off-chain reassembler already decodes matters more than the documentation convenience of a positional mirror. Decode each struct against its own ABI; never assume the mirror extends to new fields.

It is a **distinct type** from `NAV`, deliberately: a partial result must not be assignable or decodable where a complete NAV is expected. Both reads run the same underlying `computeNav`, with the validated slice in place of the full adapter set, so a complete read and the sum of its slices agree because they are literally the same code path.

{% hint style="warning" %}
**Only `value` composes by addition.** The health signals do **not**. `stalePriceAssets`, `irregularPriceAssets` and `monitorsUnhealthyPriceAssets` cover only the assets that slice touched, and the three booleans are reported per slice. A caller that reassembles the total but checks the flags on one slice — the last one, or whichever it kept — silently drops every staleness, depeg and sequencer-down signal the other slices carried, and ends up with a NAV that looks healthy. Union the asset arrays and OR the booleans across **every** slice.

**Pass a caller-supplied adapter set, never an index range.** The adapter list is maintained with swap-and-pop, so removing an adapter moves the last one down into the freed slot. A caller paging `[0,10)` then `[10,20)` across a governance transaction therefore **silently skips** the moved adapter — both pages look well-formed and the total is short by its positions. Because elements only ever move down, the hazard is a missed adapter, never a double-counted one.

A slice is rejected outright rather than returning a quietly wrong `value`: `EmptyAdapterSlice()`, `AdapterNotRegistered(address)` for anything absent from `getAllAdapters()`, and `DuplicateAdapterInSlice(address)` for a repeat.
{% endhint %}

**A meta-adapter is the atomic unit.** Naming one in a slice includes all of its governed instances; there is no paging within a single adapter, since the instance set is itself swap-and-pop mutable and an instance range would carry the identical hazard. If one meta-adapter alone exceeds the budget, pagination cannot help.

</details>

<details>

<summary><strong>Price-feed snapshot — <code>PriceFeedData</code></strong></summary>

```solidity
struct PriceFeedData {
    address priceFeed;             // the primary (pricing) feed
    IPrices.PriceType priceType;   // Chainlink / Redstone / API3 / Custom
    int256  price;                 // 0 when stale
    uint8   decimals;
    uint256 chainlinkHeartbeat;    // Custom feeds: the governing leg's heartbeat, not the registered value
    uint256 updatedAt;             // Custom feeds: the governing Chainlink leg's timestamp
    bool    stale;                 // true if the primary feed is stale or sequencer down
    bool    sequencerDown;
    uint256 healthyFeedCount;      // pricing feeds that passed all staleness gates this read
    bool    irregular;             // primary diverged from monitors / peg beyond tolerance (signal only)
    uint256 divergenceBps;         // worst-case primary-vs-monitor deviation, in bps (0 if no healthy monitor)
    uint256 monitorFeedCount;      // monitor feeds that passed all staleness gates this read
}
```

For a **Custom** feed — a composite of a rate and a base/USD leg — `(updatedAt, chainlinkHeartbeat)` report the **governing leg**: the underlying Chainlink leg with the greatest `age / heartbeat` ratio, i.e. the one closest to its own staleness deadline. The pair is therefore a real, matched pair from one actual oracle rather than a mix of two, and it reduces to the single leg's values for a single-leg feed. Which leg governs can change between reads as the ratios cycle, so a two-leg feed's reported heartbeat may alternate between its legs' values. See [Price feeds](/funds/infrastructure/onchain-accounting/price-feeds#governing-leg-freshness).

{% hint style="warning" %}
**`(updatedAt, chainlinkHeartbeat)` are advisory; `stale` is the authoritative verdict.** The pair describes only the governing oracle leg's push freshness. `stale` additionally folds in the rate answer's validity, the L2 sequencer state, and the feed's own configured heartbeat gate — so the pair can read fresh (`age < heartbeat`) while `stale` is `true`. Always gate on `stale` / `sequencerDown`; never recompute staleness from the pair alone.
{% endhint %}

</details>

{% hint style="warning" %}
The stable reconciliation key is `keccak256(abi.encode(chainId, protocolSubId, positionId, balanceAsset.asset, positionKind))` (see [Position identity](/funds/infrastructure/onchain-accounting/concepts/position-identity)). It keys on **`protocolSubId`** — the product-level slug — not `protocolId`; `protocolBrand` and `protocolId` are display/grouping layers only. `isLocked` marks a leg that is not withdrawable right now (the pending/cooling portion of a withdrawal queue or cooldown) but is **excluded** from the key — it is a mutable per-leg attribute (a leg flips `true→false` as it finalizes), so a withdrawal queue's claimable and locked legs share one key and **sum**. `positionInstanceId` (the ephemeral per-item handle — an NFT id, a withdrawal-request id, an exit ticket) is likewise **excluded**, so the legs of one anchor sum and identity survives item churn. Labels are **not** on the position — they are computed lazily by the **verbose read methods** (`getAccountPositionsVerbose` / `getAccountNavVerbose`), which call each adapter's [`IPositionDescribable`](/funds/infrastructure/onchain-accounting/concepts/position-identity#human-readable-labels) (`protocolName()` + `positionLabels(positionId)`) and render the full breadcrumb `[protocolName, ...labels]`. These live on the NAV Calculator itself (there is no separate lens contract).
{% endhint %}

#### Read functions

| Function                                                      | Purpose                                                                                                                                       |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `getAccountNav`                                               | Total chain-local NAV for an account in a quote currency (`address(0)` = USD)                                                                 |
| `getAccountNavForAdapters`                                    | NAV over a **caller-supplied subset** of adapters, for paging a read too large for one call (returns `PartialNAV`)                            |
| `getAccountPositions`                                         | One priced `Position` per balance entry reported for an account                                                                               |
| `getAccountPositionsForAsset`                                 | Priced positions for a single asset across all adapters that report it                                                                        |
| `getAccountNavVerbose`                                        | `getAccountNav` plus per-position `protocolName` + `labels[]` (returns `VerboseNAV`)                                                          |
| `getAccountPositionsVerbose`                                  | `getAccountPositions` enriched with `protocolName` + `labels[]` (returns `VerbosePosition[]`)                                                 |
| `getAccountPositionsForAssetVerbose`                          | Single-asset verbose positions (returns `VerbosePosition[]`)                                                                                  |
| `healthCheck`                                                 | Sequencer status + currently-stale **and** currently-irregular assets, without a full NAV                                                     |
| `getPriceData`                                                | Detailed data for an asset's primary feed + divergence signal (`price`, `stale`, `irregular` …)                                               |
| `getPriceDataNoDivergence`                                    | Same as `getPriceData` but skips the monitor read (used on the base/USD hot path)                                                             |
| `getPriceDivergence`                                          | Live primary-vs-monitor divergence read (median, worst-case bps, `irregular`, direction)                                                      |
| `hasPositions`                                                | Whether an account has any non-zero position                                                                                                  |
| `getAssetsWithPositions`                                      | Assets for which an account holds a non-zero position                                                                                         |
| `calculateValue`                                              | Value of a set of (asset, amount) pairs in a quote currency                                                                                   |
| `getAssetInfo`                                                | One asset's [classification](/funds/infrastructure/onchain-accounting/concepts/asset-classification) **and** display labels, in a single call |
| `getRegisteredAsset`                                          | Resolves **one** asset to its `(address, symbol, decimals)` in O(1), plus a `found` flag                                                      |
| `getRegisteredAssets` · `isAssetRegistered` · `getAssetCount` | Asset-registry introspection                                                                                                                  |
| `usdDecimals` · `version`                                     | USD value decimals (e.g. 8); implementation version                                                                                           |

<details>

<summary><strong>Full read-function signatures</strong></summary>

```solidity
/// Total chain-local NAV for an account in the given quote currency (address(0) = USD).
function getAccountNav(address account, address quoteAsset)
    external view returns (NAV memory nav);

/// NAV over a caller-supplied subset of registered adapters. Pass the SAME quoteAsset to every
/// slice. Only `value` sums; union the asset arrays and OR the booleans across all slices.
function getAccountNavForAdapters(address account, address quoteAsset, address[] calldata adapters)
    external view returns (PartialNAV memory partialNav);

/// One Position per balance entry reported for an account, with pricing.
function getAccountPositions(address account, address quoteAsset)
    external view returns (Position[] memory positions);

/// Positions for a single asset across all adapters that report it.
function getAccountPositionsForAsset(address account, address asset, address quoteAsset)
    external view returns (Position[] memory positions);

/// Verbose variants — positions enriched with protocolName + labels[] (see IPositionDescribable).
function getAccountNavVerbose(address account, address quoteAsset)
    external view returns (VerboseNAV memory verboseNav);
function getAccountPositionsVerbose(address account, address quoteAsset)
    external view returns (VerbosePosition[] memory positions);
function getAccountPositionsForAssetVerbose(address account, address asset, address quoteAsset)
    external view returns (VerbosePosition[] memory positions);

/// Sequencer status, all currently-stale, and all currently-irregular price assets, without a full NAV.
/// NOTE: deliberately carries no monitorsUnhealthyPriceAssets — see the note under the NAV struct.
function healthCheck()
    external view returns (bool sequencerDown, address[] memory stalePriceAssets, address[] memory irregularPriceAssets);

/// Detailed data for an asset's primary feed + divergence signal (price, decimals, stale, irregular, …).
function getPriceData(address underlyingAsset)
    external view returns (PriceFeedData memory priceFeedData);

/// Same as getPriceData but skips the monitor/divergence read (used by custom feeds on the base/USD hot path).
function getPriceDataNoDivergence(address underlyingAsset)
    external view returns (PriceFeedData memory priceFeedData);

/// Live primary-vs-monitor divergence read for an asset.
function getPriceDivergence(address underlyingAsset)
    external view
    returns (
        int256  primaryPrice,        // 8-dp primary price (0 if the primary is stale)
        int256  monitorMedian,       // median of the healthy monitor prices
        uint256 divergenceBps,       // worst-case primary-vs-monitor deviation, in bps
        bool    irregular,           // divergence beyond tolerance, or (stablecoins) off the $1 peg
        uint256 monitorFeedCount,    // healthy monitor feeds this read
        bool    primaryStale,        // primary feed stale or sequencer down (suppresses the signal)
        bool    primaryAboveMonitors // primaryPrice > monitorMedian (directionality)
    );

/// One asset's classification AND display labels, forwarded to the AssetKindRegistry this NAV
/// points at. `labels` may be empty — a real answer, not a gap. Reverts AssetNotClassified(asset)
/// if unclassified, and reverts if no registry is set.
/// Declared on NAVCalculator itself, NOT on INAVCalculator.
function getAssetInfo(address asset)
    external view returns (PegKind, AccrualKind, string[] memory labels);

/// The registry this NAV forwards classification reads to.
function assetKindRegistry() external view returns (address);

/// Monitor (divergence-only) feeds and per-asset tolerances.
function getMonitorFeeds(address asset) external view returns (IPrices.PriceFeedConfig[] memory);
function getMonitorFeedCount(address asset) external view returns (uint256);
function getAssetPricing(address asset) external view returns (IPrices.AssetPricing memory);

/// Whether an account has any non-zero position.
function hasPositions(address account) external view returns (bool has);

/// Assets for which an account holds at least one non-zero position.
function getAssetsWithPositions(address account) external view returns (address[] memory assets);

/// Value of a set of (asset, amount) pairs in a quote currency.
function calculateValue(address[] memory assets, uint256[] memory amounts, address quoteAsset)
    external view returns (int256 value, bool quoteAssetStale);

/// Registry introspection.
/// Resolve ONE asset in O(1). Non-reverting: `found == false` for an unregistered asset.
/// Prefer this over getRegisteredAssets() — see the note below.
function getRegisteredAsset(address asset) external view returns (Asset memory assetInfo, bool found);
function getRegisteredAssets() external view returns (Asset[] memory assets);
function isAssetRegistered(address asset) external view returns (bool);
function getAssetCount() external view returns (uint256);
function usdDecimals() external view returns (uint8);   // e.g. 8
function version() external view returns (uint64);
```

</details>

{% hint style="warning" %}
**To resolve one asset, use `getRegisteredAsset`, not `getRegisteredAssets`.** The plural getter copies the **entire** registry across the ABI boundary, so resolving M tokens by calling it M times pays M full fetches — and the cost is super-linear, not linear, because each call allocates a fresh N-element array. At scale that is enough to exhaust an adapter's gas stipend and turn a NAV read into a **revert**, not merely a slow one. `getRegisteredAsset` is O(1).

It is also **non-reverting by design**: an unregistered asset returns `found == false` rather than reverting, because "not registered" is an ordinary outcome on an adapter's emission path — the adapter simply skips that leg. A reverting getter would turn every unregistered token an adapter happens to encounter into a failed instance read, converting a skipped leg into a dropped position.
{% endhint %}

#### Examples

Illustrative calls and returns. Values are example data (not live); addresses are abbreviated. The account is a portfolio Safe, `USDC` = `0xA0b8…eB48` (6 dp), `WETH` = `0xC02a…6Cc2` (18 dp), and the USD quote asset is `address(0)`.

```
getAccountNav(0x1F98…E984, address(0))
→ NAV {
    value:                1284530000000,   // 12,845.30 USD  (value / 1e8)
    quoteAsset:           { 0x0000…0000, "USD", 8 },
    timestamp:            1718901234,
    stalePriceAssets:     [],              // all primary feeds fresh
    sequencerDown:        false,
    quoteAssetStale:      false,
    irregularPriceAssets: [],              // no primary diverged from its monitors / peg
    quoteAssetIrregular:  false,
    monitorsUnhealthyPriceAssets: []       // every configured monitor answered — the line above means something
  }
```

```
getAccountPositions(0x1F98…E984, address(0))
→ [
    Position {
      balanceAsset:   { 0xA0b8…eB48, "USDC", 6 },
      balanceAdapter: 0x081D…dC39,        // ERC20Default (wallet)
      amount:         5000000000,         // 5,000 USDC
      isDebt:         false,
      quoteAsset:     { 0x0000…0000, "USD", 8 },
      value:          500000000000,       // 5,000.00 USD
      price:          100000000,          // $1.00 (8 dp)
      priceDecimals:  8,
      stale:          false,
      protocolBrand:  keccak256("wallet"),
      protocolId:     keccak256("wallet"),
      protocolSubId:  keccak256("wallet"),
      positionId:     abi.encode(0xA0b8…eB48),
      positionKind:   0,                   // Wallet
      isLocked:       false,
      positionInstanceId: bytes32(0),
      irregular:      false,
      quoteAssetIrregular: false
    },
    Position {
      balanceAsset:   { 0xC02a…6Cc2, "WETH", 18 },
      balanceAdapter: 0x9aBc…7e58,        // Morpho Markets (meta)
      amount:         2000000000000000000, // 2.0 WETH supplied
      isDebt:         false,
      value:          784530000000,       // 7,845.30 USD
      price:          392265000000,       // $3,922.65 (8 dp)
      priceDecimals:  8,
      stale:          false,
      protocolBrand:  keccak256("morpho"),
      protocolId:     keccak256("morpho"),
      protocolSubId:  keccak256("morpho-markets"),
      positionId:     abi.encode(0x3a85…f21d),  // bytes32 marketId
      positionKind:   1,                   // Supplied
      isLocked:       false,
      positionInstanceId: bytes32(0),
      irregular:      false,
      quoteAssetIrregular: false
    }
  ]
```

```
getAccountPositionsForAsset(0x1F98…E984, 0xC02a…6Cc2, address(0))   // WETH only
→ [ Position { …WETH supplied on Morpho, value 7,845.30 USD… } ]
```

```
getPriceData(0xC02a…6Cc2)   // WETH: Chainlink primary + Redstone monitor
→ PriceFeedData {
    priceFeed:          0x5f4e…8419,   // Chainlink ETH/USD (primary)
    priceType:          0,             // Chainlink
    price:              392265000000,  // $3,922.65 (8 dp)
    decimals:           8,
    chainlinkHeartbeat: 3600,
    updatedAt:          1718901200,
    stale:              false,
    sequencerDown:      false,
    healthyFeedCount:   1,             // 1 primary feed fresh
    irregular:          false,         // within the 50-bps divergence tolerance
    divergenceBps:      11,            // primary vs Redstone monitor
    monitorFeedCount:   1              // 1 monitor feed fresh
  }
```

```
getAccountPositionsVerbose(0x1F98…E984, address(0))   // same positions, with labels
→ [
    VerbosePosition {
      position:     { …WETH supplied on Morpho… },
      protocolName: "Morpho",
      labels:       ["Market", "wstETH/USDC"]
    },
    …
  ]
```

```
healthCheck()
→ (false, [], [])                     // sequencer up, no stale assets, no irregular assets
→ (false, [0x6B17…1d0F], [])          // example: DAI primary feed stale
→ (false, [], [0xae78…86CA])          // example: wstETH primary diverged from its monitors

hasPositions(0x1F98…E984)             → true
getAssetsWithPositions(0x1F98…E984)   → [0xA0b8…eB48, 0xC02a…6Cc2]   // USDC, WETH

calculateValue([0xC02a…6Cc2], [1000000000000000000], address(0))    // 1 WETH in USD
→ (392265000000, false)               // 3,922.65 USD; quote-asset feed not stale
```

```
getRegisteredAssets()
→ [ {0xEeee…EEeE,"ETH",18}, {0xA0b8…eB48,"USDC",6}, {0xC02a…6Cc2,"WETH",18}, … ]

getAssetCount()                       → 37
isAssetRegistered(0xC02a…6Cc2)        → true
usdDecimals()                         → 8
version()                             → 2
```

### Stale prices and sequencer status

The `NAV` struct carries hard reliability signals — `stalePriceAssets`, `sequencerDown`, and `quoteAssetStale` — that must **block** a pricing read, plus two soft signals: `irregularPriceAssets` (and `quoteAssetIrregular`), which flags a price the primary and monitor feeds disagree on, and `monitorsUnhealthyPriceAssets`, which flags assets where **no monitor answered at all**, so the disagreement check never ran. See [Stale prices & sequencer](/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer) for the full model, and use `healthCheck()` to poll chain health without computing a full NAV — noting that `healthCheck` does not carry the third array.

***

## Admin / Manager API

Configuration is privileged and held by the security council Safe. Most mutations require the **`MANAGER`** role; the sequencer-feed setters and proxy upgrades require **`DEFAULT_ADMIN_ROLE`**. Both roles are held by the security council Safe. All batch calls are **all-or-nothing** — if any item fails its check, the whole transaction reverts.

#### Assets & price feeds (MANAGER)

An asset enters the system with its primary feed via `registerAsset`, carries **one pricing feed** plus optional **monitor** feeds and tolerances, and leaves via `unregisterAsset`. See [Price feeds](/funds/infrastructure/onchain-accounting/price-feeds) for the primary-feed + divergence-monitor model.

| Function                                                                    | What it does · key reverts                                                                                                                                                                                                                                                                                                                                                       |
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `registerAsset(asset, priceFeed, priceType, heartbeat)`                     | Registers a new asset **and** its primary price feed in one call; reads `symbol`/`decimals` from the ERC-20 (or the native-token constant) and adds the asset to the default (wallet) adapter's inclusion list. Reverts `InvalidArguments` for an address with no code (the native-token sentinel aside), so a typo'd or not-yet-deployed token cannot be registered.            |
| `addPriceFeed(asset, priceFeed, priceType, heartbeat)`                      | Adds another **pricing** feed to an already-registered asset (transitional; production keeps one). Reverts `PriceFeedAlreadyRegistered` (same feed twice) or `PriceFeedDecimalsMismatch` (a new feed's decimals must equal the existing feeds'); `heartbeat` must be non-zero.                                                                                                   |
| `removePriceFeed(asset, priceFeed)`                                         | Removes one pricing feed by address. Reverts `PriceFeedNotFound`, or `CannotRemoveLastFeed` if it is the only pricing feed left.                                                                                                                                                                                                                                                 |
| `removePriceFeedAt(asset, index)`                                           | Same, by array index. Reverts `PriceFeedIndexOutOfBounds` or `CannotRemoveLastFeed`.                                                                                                                                                                                                                                                                                             |
| `setDivergenceTolerance(asset, bps)`                                        | Sets the primary-vs-monitor divergence tolerance. Must be in `(0, 10000]`; `0` **reverts** `InvalidTolerance` — it does not disable the check. Required before any monitor feed can be added.                                                                                                                                                                                    |
| `setPegTolerance(asset, bps)`                                               | Sets the $1-peg tolerance (stablecoins). Must be in `[0, 10000]`; `0` is accepted and disables the peg check (only a value above `10000` reverts `InvalidTolerance`). **Also the switch that decides the asset's** [**peg classification**](/funds/infrastructure/onchain-accounting/concepts/asset-classification#the-peg-axis-is-derived-not-stored) — crossing zero flips it. |
| `setAssetKindRegistry(registry)`                                            | Points this NAV at the chain's [`AssetKindRegistry`](/funds/infrastructure/onchain-accounting/concepts/asset-classification). Rejects `address(0)` (`InvalidArguments`). Re-settable: the registry is a plain contract, so replacing it is a redeploy plus this call.                                                                                                            |
| `addMonitorFeed(asset, priceFeed, priceType, heartbeat)`                    | Adds a divergence-only monitor feed. Reverts `DivergenceToleranceRequired` (no tolerance set), `MonitorFeedDecimalsMismatch` (monitor must report 8 decimals), or `MonitorFeedAlreadyRegistered`.                                                                                                                                                                                |
| `removeMonitorFeed(asset, priceFeed)` · `removeMonitorFeedAt(asset, index)` | Removes a monitor feed by address or index (`MonitorFeedNotFound`). Monitors are optional — the last one may be removed.                                                                                                                                                                                                                                                         |
| `unregisterAsset(asset)`                                                    | Fully removes the asset: clears **all** its pricing and monitor feeds, removes it from the default-adapter list, and drops it from the asset registry. Use only after the asset is no longer held anywhere.                                                                                                                                                                      |

#### Balance adapters (MANAGER)

Plain (single-scope) adapters only — meta-adapters use the next group. See [Balance adapters](/funds/infrastructure/onchain-accounting/balance-adapters).

| Function                                    | What it does · key reverts                                                                                                                                                                                                                                           |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addBalanceAdapters(address[] adapters)`    | Batch-registers **plain** adapters into the global registry. Reverts `InvalidArguments` (empty list / zero address), `DuplicateBalanceAdapter`, or `MetaAdapterNotAllowed` if any adapter implements `IMetaBalanceAdapter` (those must use `addMetaBalanceAdapter`). |
| `removeBalanceAdapters(address[] adapters)` | Batch-removes adapters from the registry. Reverts if any is not registered or is the built-in `ERC20Default` adapter (which cannot be removed).                                                                                                                      |

#### Meta-adapters & their instances (MANAGER)

A meta-adapter is registered **with** its initial instance set; the set is then grown or shrunk incrementally. See [Meta balance adapters](/funds/infrastructure/onchain-accounting/meta-balance-adapters).

| Function                                              | What it does · key reverts                                                                                                                                                                                                                                                            |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addMetaBalanceAdapter(adapter, bytes32[] instances)` | Registers a meta-adapter **and** seeds its instance set atomically. The adapter must implement `IMetaBalanceAdapter` (else `NotMetaAdapter` / `InvalidBalanceAdapter`). Reverts `DuplicateBalanceAdapter` or `DuplicateMetaInstance`. An empty instance list is allowed (seed later). |
| `addMetaInstances(adapter, bytes32[] instances)`      | Adds instance coordinates to a registered meta-adapter. Reverts `DuplicateMetaInstance` (already present or repeated in the batch — a duplicate would double-count NAV), `InvalidArguments` (empty), or `NotMetaAdapter`.                                                             |
| `removeMetaInstances(adapter, bytes32[] instances)`   | Removes coordinates. Reverts `MetaInstanceNotFound`, `InvalidArguments` (empty), or `NotMetaAdapter`. Order is not preserved.                                                                                                                                                         |

Adding a market/vault/pool to coverage is thus an `addMetaInstances` transaction — never a new contract deploy.

#### Default (wallet) adapter inclusion (MANAGER)

| Function                                | What it does                                                                                                                                                     |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `excludeAssetFromDefaultAdapter(asset)` | Stops the built-in `ERC20Default` adapter from reporting **wallet** balances for an asset, without unregistering the asset (its protocol positions still count). |
| `includeAssetInDefaultAdapter(asset)`   | Re-enables wallet-balance reporting for the asset.                                                                                                               |

#### L2 sequencer & upgrades (DEFAULT\_ADMIN\_ROLE)

| Function                                      | What it does · key reverts                                                                                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `setChainlinkL2SequencerUptimeFeed(feed)`     | Sets the Chainlink L2 sequencer uptime feed (`address(0)` on L1). Admin-gated because disabling sequencer detection on an L2 has upgrade-level impact. |
| `setChainlinkL2SequencerGracePeriod(seconds)` | Sets the post-recovery grace period. Reverts `UptimeFeedNotSet` if no uptime feed is configured.                                                       |
| *UUPS upgrade*                                | `upgradeToAndCall(...)` (UUPSUpgradeable) is authorized by `DEFAULT_ADMIN_ROLE`; the implementation can change, the proxy address never does.          |

<details>

<summary><strong>Admin / Manager function signatures</strong></summary>

```solidity
// Assets & price feeds (MANAGER)
function registerAsset(address asset, address priceFeed, IPrices.PriceType priceType, uint256 chainlinkHeartbeat) external;
function addPriceFeed(address asset, address priceFeed, IPrices.PriceType priceType, uint256 chainlinkHeartbeat) external;
function removePriceFeed(address asset, address priceFeed) external;
function removePriceFeedAt(address asset, uint256 index) external;
function unregisterAsset(address underlyingAsset) external;

// Divergence monitors & tolerances (MANAGER)
function setDivergenceTolerance(address asset, uint32 divergenceToleranceBps) external;
function setPegTolerance(address asset, uint32 pegToleranceBps) external;
function addMonitorFeed(address asset, address priceFeed, IPrices.PriceType priceType, uint256 chainlinkHeartbeat) external;
function removeMonitorFeed(address asset, address priceFeed) external;
function removeMonitorFeedAt(address asset, uint256 index) external;

// Default (wallet) adapter inclusion (MANAGER)
function excludeAssetFromDefaultAdapter(address asset) external;
function includeAssetInDefaultAdapter(address asset) external;

// Balance adapters (MANAGER)
function addBalanceAdapters(address[] calldata adapters) external;          // plain adapters only
function removeBalanceAdapters(address[] calldata adapters) external;
function addMetaBalanceAdapter(address adapter, bytes32[] calldata instances) external;  // meta-adapter + seed
function addMetaInstances(address adapter, bytes32[] calldata instances) external;
function removeMetaInstances(address adapter, bytes32[] calldata instances) external;

// L2 sequencer (DEFAULT_ADMIN_ROLE)
function setChainlinkL2SequencerUptimeFeed(address newChainlinkL2UptimeFeed) external;
function setChainlinkL2SequencerGracePeriod(uint256 newSequencerGracePeriod) external;
```

</details>

{% hint style="info" %}
Plain adapters are registered with `addBalanceAdapters`; **meta-adapters** (one adapter covering many protocol instances) are registered with `addMetaBalanceAdapter` and have their queried instance set adjusted with `addMetaInstances` / `removeMetaInstances`. See [Balance adapters](/funds/infrastructure/onchain-accounting/balance-adapters).
{% endhint %}

***

## Multichain aggregation

The offchain **NAV Worker** collects per-chain NAV values and sums them:

1. Call `getAccountNav` on every chain the fund operates on.
2. Verify `stalePriceAssets` is empty, `sequencerDown` is `false`, and `quoteAssetStale` is `false` on every chain.
3. If any chain is unhealthy, the global NAV is withheld until the condition clears.
4. Otherwise, sum all per-chain `value` fields to produce the global fund NAV.
5. Treat a non-empty `irregularPriceAssets` as an **alert**, not a hard block: the value is still usable, but the divergence should be investigated before it widens into a stale/blocking condition.
6. Treat a non-empty `monitorsUnhealthyPriceAssets` as an alert on the **check itself** rather than on the price: those assets lost their cross-check this read, so an empty `irregularPriceAssets` says nothing about them. Reconcile the two lists together — an asset in the third array is unverified, not verified-clean.

{% hint style="info" %}
See [Code examples](/funds/infrastructure/onchain-accounting/code-examples) for TypeScript and Python examples of the single-chain and multichain read paths.
{% endhint %}


# Concepts

Cross-cutting models: position identity and the stale-price / sequencer reliability signals.

Cross-cutting models that apply across the NAV Calculator, price feeds, and balance adapters: how positions are **identified** in a stable, machine-readable way, and how a reading signals that it is **unreliable** (stale prices, an unhealthy quote asset, or a down L2 sequencer).

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Position identity</strong></td><td>The 3-level protocol taxonomy, the <code>positionId</code> / <code>positionKind</code> / <code>isLocked</code> fields, decode schemas, and the reconciliation key.</td><td><a href="/funds/infrastructure/onchain-accounting/concepts/position-identity">Position identity</a></td></tr><tr><td><strong>Stale prices &#x26; sequencer</strong></td><td>Stale-feed handling, the quote-asset fallback, and the L2 sequencer uptime check.</td><td><a href="/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer">Stale prices &amp; sequencer</a></td></tr></tbody></table>


# Position identity

How positions are identified: the 3-level protocol taxonomy, the positionId / positionKind / isLocked fields, the ephemeral positionInstanceId, and the reconciliation key.

Every position the NAV Calculator reports carries a **machine-readable identity** so consumers can branch, index, and reconcile without parsing display strings. The stable identity is the typed key **`protocolSubId` + `positionId` + `balanceAsset.asset` + `positionKind`** on each `PositionBalance` / `Position`, scoped per `chainId`.

**Source:** [`INAVCalculator.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/nav/INAVCalculator.sol) · [`ProtocolIds.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/utils/ProtocolIds.sol)

***

## The identity fields

Each position carries a **3-level protocol taxonomy** (`protocolBrand` ⊃ `protocolId` ⊃ `protocolSubId`). Only the most specific level, `protocolSubId`, is part of the identity key; the two broader levels exist for grouping and roll-up.

| Field                | Type      | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `protocolBrand`      | `bytes32` | `keccak256` of the **version-stripped brand** slug (e.g. `aave`, `balancer`, `uniswap`). Broadest grouping layer — display/roll-up only, **not** in the identity key.                                                                                                                                                                                                                                                                                                                                               |
| `protocolId`         | `bytes32` | `keccak256` of the **ecosystem** slug, version included (e.g. `aave-v3`, `balancer-v2`). Mid grouping layer — **not** in the identity key (functionally determined by `protocolSubId`).                                                                                                                                                                                                                                                                                                                             |
| `protocolSubId`      | `bytes32` | `keccak256` of the **product** slug (e.g. `aave-v3-lending`, `balancer-v3-pools`). **In the identity key** — it selects the `positionId` decode schema: one slug ⇒ exactly one schema, so no runtime sniffing is needed.                                                                                                                                                                                                                                                                                            |
| `positionId`         | `bytes`   | ABI-encoded **static** position coordinates. How to decode it is fully determined by `protocolSubId`.                                                                                                                                                                                                                                                                                                                                                                                                               |
| `positionKind`       | `enum`    | Semantic action family (below). `isDebt` remains the canonical NAV-sign signal; `positionKind` adds category context.                                                                                                                                                                                                                                                                                                                                                                                               |
| `isLocked`           | `bool`    | `true` when the leg is **not withdrawable right now** — a pending/cooling withdrawal request or cooldown. `false` for every other leg. A **mutable per-leg attribute, NOT part of the identity key** — a leg flips `true→false` as its portion finalizes, so keying on it would split one economic position. A withdrawal queue's claimable (`isLocked=false`) and locked (`isLocked=true`) legs share one key and are **summed** in reconciliation; read `isLocked` per leg for the claimable-vs-locked breakdown. |
| `positionInstanceId` | `bytes32` | **Ephemeral** per-item handle — an NFT `tokenId`, a withdrawal-request id, or a StakeWise exit ticket; `bytes32(0)` when there is no per-item handle. **Excluded** from the stable identity key by design, so the legs of one anchor sum and identity survives item churn.                                                                                                                                                                                                                                          |

{% hint style="info" %}
`balanceAdapter` (the address that reported a position) is **provenance, not identity** — the same logical position can move to a different adapter across a redeploy. `protocolBrand` and `protocolId` are **grouping only**. The stable identity is `protocolSubId` + `positionId` + `balanceAsset.asset` + `positionKind` (per chain).
{% endhint %}

***

## PositionKind

```solidity
enum PositionKind {
    Wallet,      // plain ERC20 / native balance held directly
    Supplied,    // credit deployed into a protocol: lending supply, vault shares, unstaked LP principal
    Borrowed,    // debt owed to a protocol (isDebt = true)
    Collateral,  // credit locked as collateral
    Staking,     // principal locked behind an unstake action (Safety Module, gauge/Convex-staked LP, …)
    Rewards,     // accrued but unclaimed reward tokens (CRV, CVX, BAL, COMP, …)
    Fees         // uncollected protocol / LP fees (e.g. Uniswap V3 position fees)
}
```

The kind reflects the **outermost** action family — what the account does to enter/exit the position. Gauge-staked LP is `Staking` regardless of the LP wrapped inside; the inner exposure stays recoverable from `protocolId` / `positionId`. Unstaked LP sitting in the wallet is `Supplied`.

***

## Protocol taxonomy catalogue

Slugs are defined in [`src/utils/ProtocolIds.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/utils/ProtocolIds.sol); each constant is `keccak256` of the slug. The **`protocolSubId`** (product) column is the one that enters the identity key and selects the decode schema; `protocolId` (ecosystem) and `protocolBrand` (brand) group it. Single-product ecosystems collapse — brand = id = subId.

| Family                       | `protocolId` (ecosystem)                          | `protocolSubId` (product)                                                                              |
| ---------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Wallet                       | `wallet`                                          | `wallet`                                                                                               |
| Aave V3                      | `aave-v3`                                         | `aave-v3-lending` · `aave-v3-safety-module` · `aave-v3-umbrella`                                       |
| Spark                        | `spark`                                           | `spark-lend`                                                                                           |
| Morpho                       | `morpho`                                          | `morpho-vaults` · `morpho-markets`                                                                     |
| Balancer V3                  | `balancer-v3`                                     | `balancer-v3-pools` · `balancer-v3-gauges`                                                             |
| Balancer V2                  | `balancer-v2`                                     | `balancer-v2-pools` · `balancer-v2-gauges`                                                             |
| Curve                        | `curve`                                           | `curve-pools` · `curve-gauges`                                                                         |
| Convex                       | `convex`                                          | `convex-curve-lp-staking`                                                                              |
| Uniswap V3 / V2              | `uniswap-v3` · `uniswap-v2`                       | `uniswap-v3` · `uniswap-v2`                                                                            |
| PancakeSwap V3 / V2          | `pancakeswap-v3` · `pancakeswap-v2`               | `pancakeswap-v3` · `pancakeswap-v2`                                                                    |
| Gearbox V3                   | `gearbox-v3`                                      | `gearbox-v3-markets` · `gearbox-v3-credit-accounts`                                                    |
| Euler                        | `euler`                                           | `euler-vaults`                                                                                         |
| Compound V3                  | `compound-v3`                                     | `compound-v3-comets`                                                                                   |
| Fluid                        | `fluid`                                           | `fluid-ftokens` · `fluid-vaults`                                                                       |
| StakeWise V3                 | `stakewise-v3`                                    | `stakewise-v3-vaults` · `stakewise-v3-exit`                                                            |
| Nexus Mutual staking         | `nexus-mutual`                                    | `nexus-mutual-staking-pools`                                                                           |
| Generic ERC-4626             | `erc4626`                                         | `erc4626`                                                                                              |
| Cap                          | `cap`                                             | `cap`                                                                                                  |
| Withdrawal / cooldown queues | `lido` · `stader` · `kelp` · `etherfi` · `ethena` | `lido-withdrawal` · `stader-withdrawal` · `kelp-withdrawal` · `etherfi-withdrawal` · `ethena-cooldown` |

For grouping, `protocolBrand` strips the version: `aave-v3 → aave`, `balancer-v2`/`balancer-v3 → balancer`, `uniswap-v3`/`uniswap-v2 → uniswap`, `pancakeswap-* → pancakeswap`, `gearbox-v3 → gearbox`, `stakewise-v3 → stakewise`, `compound-v3 → compound`. Single-version protocols reuse their ecosystem id as the brand.

***

## positionId decode schemas

All `positionId` values are `abi.encode(...)`. The schema is keyed by `protocolSubId`:

| `protocolSubId`                                                                                              | `positionId` schema                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `wallet`                                                                                                     | `(address asset)`                                                                                                                                                               |
| `aave-v3-lending` · `spark-lend`                                                                             | `(address pool)` — one position per pool; the reserve is carried in `balanceAsset`, not the `positionId`                                                                        |
| `aave-v3-safety-module` · `aave-v3-umbrella`                                                                 | `(address stakeToken)` — the two staking legs are told apart by `isLocked`, the reward leg by `positionKind`                                                                    |
| `morpho-vaults` · `gearbox-v3-markets` · `euler-vaults` · `erc4626` · `stakewise-v3-vaults` · `fluid-vaults` | `(address vault)`                                                                                                                                                               |
| `gearbox-v3-credit-accounts`                                                                                 | `(address creditAccount)` — one anchor per Credit Account; the Borrowed leg and each Collateral leg share it                                                                    |
| `cap`                                                                                                        | `(address token)` — the cUSD leg or the stcUSD leg                                                                                                                              |
| `morpho-markets`                                                                                             | `(bytes32 marketId)`                                                                                                                                                            |
| `balancer-v3-pools` · `curve-pools`                                                                          | `(address pool)`                                                                                                                                                                |
| `balancer-v3-gauges` · `balancer-v2-gauges` · `curve-gauges`                                                 | `(address gauge)`                                                                                                                                                               |
| `convex-curve-lp-staking`                                                                                    | `(address rewarder)` — Convex `BaseRewardPool`                                                                                                                                  |
| `balancer-v2-pools`                                                                                          | `(bytes32 poolId)`                                                                                                                                                              |
| `uniswap-v3` · `pancakeswap-v3`                                                                              | `(address pool)` — one per fee-tier pool; the NFT `tokenId` is in `positionInstanceId`, not the `positionId`                                                                    |
| `uniswap-v2` · `pancakeswap-v2`                                                                              | `(address pair)`                                                                                                                                                                |
| `compound-v3-comets` · `fluid-ftokens`                                                                       | `(address market)`                                                                                                                                                              |
| `nexus-mutual-staking-pools`                                                                                 | `(address stakingPool)` — the NFT `tokenId` is in `positionInstanceId`                                                                                                          |
| `stader-withdrawal`                                                                                          | `(address userWithdrawalManager)` — one anchor; one leg per open request (`positionInstanceId` = request id)                                                                    |
| `kelp-withdrawal`                                                                                            | `(address withdrawalManager)` — one anchor across all payout assets; one leg per open request (`positionInstanceId` = request id), legs told apart also by `balanceAsset.asset` |
| `lido-withdrawal`                                                                                            | `(address withdrawalQueue)` — one anchor; one leg per open request (`positionInstanceId` = request id)                                                                          |
| `ethena-cooldown`                                                                                            | `(address sUSDe)` — single leg, `isLocked` toggles                                                                                                                              |
| `etherfi-withdrawal`                                                                                         | `(address withdrawRequestNFT)` — one anchor; one leg per open request (`positionInstanceId` = request NFT id)                                                                   |
| `stakewise-v3-exit`                                                                                          | `(address vault)` — static, per vault; one leg per exit ticket (`positionInstanceId` = ticket)                                                                                  |

{% hint style="info" %}
**Static positionId + per-item legs.** The withdrawal-queue and exit-queue adapters use a static `positionId` (the anchor contract above) and emit **one leg per open request**, each carrying its request id in `positionInstanceId` and its own `isLocked` (finalized/claimable → `false`, pending → `true`); the owed asset is in `balanceAsset`. The Ethena cooldown is a single leg whose `isLocked` toggles. Example — to decode a Morpho market leg: confirm `protocolSubId == keccak256("morpho-markets")`, then `marketId = abi.decode(positionId, (bytes32))`. To decode an Aave supply leg: `pool = abi.decode(positionId, (address))` — the supplied/borrowed reserve is the position's `balanceAsset.asset`.
{% endhint %}

***

## Stable reconciliation key

To match the same position across reads (and across adapter redeploys), use:

```solidity
keccak256(abi.encode(chainId, protocolSubId, positionId, balanceAsset.asset, positionKind))
```

This keys on **`protocolSubId`** (the product level), and deliberately excludes `protocolBrand`, `protocolId`, `balanceAdapter`, `positionInstanceId`, `isLocked`, `amount`, and all pricing — those either are grouping-only, change between reads, or are mutable/ephemeral, while the position's identity does not. **`isLocked` is excluded** because it is a mutable per-leg attribute (a leg flips `true→false` as its portion finalizes), so keying on it would give one economic position two keys over its lifetime.

{% hint style="info" %}
Because `isLocked` and `positionInstanceId` are **both excluded**, a withdrawal queue's claimable and locked legs — and all its per-request legs — collapse onto a single reconciliation key and **sum to the full holding**. Read `isLocked` per leg if you want the claimable-vs-locked breakdown, and append `positionInstanceId` if you want **per-item** granularity — at the cost of an ephemeral key that blinks in and out as items finalize, mint, claim, or burn. Never use those mutable/ephemeral fields for stable identity.
{% endhint %}

***

## Human-readable labels

Labels are **not** carried on the position. They are produced **lazily, on the verbose read path only**: `getAccountPositionsVerbose` / `getAccountPositionsForAssetVerbose` / `getAccountNavVerbose` call each adapter's `IPositionDescribable` — `protocolName()` and `positionLabels(positionId)` — via fail-open, gas-capped staticcalls, and return `VerbosePosition { position, protocolName, labels }`. Non-verbose reads return positions without labels (lower gas). The full breadcrumb a consumer renders is `[protocolName, ...labels]` — e.g. `["Morpho", "Market", "WETH/USDC"]` or `["Uniswap V3", "AMM Liquidity Pool", "USDC/WETH 0.3%"]`.

```solidity
interface IPositionDescribable {
    /// @notice Human-readable protocol name, e.g. "Morpho", "Uniswap V3".
    function protocolName() external view returns (string memory);

    /// @notice Display hierarchy below the protocol name, leaf last — derived purely from the static positionId.
    function positionLabels(bytes calldata positionId) external view returns (string[] memory);
}
```

Because `positionLabels` receives only the **static `positionId`**, it never includes a per-item id; the ephemeral NFT/request id lives in `positionInstanceId`. An adapter that does not implement `IPositionDescribable` (or whose call reverts) simply yields an empty `protocolName` / empty `labels`; the position's other fields are unaffected.

### Enumerating position shapes

A **configuration view** — a registry page listing what the NAV Calculator is set up to account for — has no account, so it has no `positionId` to pass to `positionLabels`. For meta-adapters the shapes *are* the governed instance set, readable via `getMetaInstances(adapter)`. For other adapters there was no enumeration primitive at all, so the only question a consumer could ask was `positionLabels("")` — which answers only for adapters whose breadcrumb happens to be `positionId`-independent. An adapter that derives labels per `positionId` correctly returns an empty array to that probe, and was therefore indistinguishable from one that was simply broken.

`IPositionEnumerable` closes that gap. It is the companion to `IPositionDescribable` and read the same way — optional, ERC-165 advertised, called through a fail-open staticcall, and **never on the NAV path**:

```solidity
interface IPositionEnumerable {
    /// @notice The positionIds this adapter can report, one per distinct position shape.
    function positionIds() external view returns (bytes[] memory);
}
```

Its one binding guarantee: **each returned id is byte-identical to the `positionId` the adapter emits for that shape**, so a configuration view can be *joined* against real positions. An id that merely labels correctly while differing from the emitted bytes silently breaks that join. The ERC-165 bit is itself the signal that an empty answer was a real answer rather than a failure.

These are **shapes, not positions**: an id being listed says nothing about whether any account holds a balance, and there are no amounts. As with labels, don't treat the enumeration as an identity or indexing mechanism — reconcile on the typed `protocolSubId` / `positionId` / `positionKind` fields.

Adapters implement it only where the shape set is bounded and derivable on-chain — pinned at construction, or a governed instance set. That is decided per adapter rather than per family: an adapter whose shapes are discovered per account (a coordinate cache fed by the position owner) has no account-independent answer and must not implement it, while one that pins its shapes at construction does, even if it is assistant-fed. Neither `positionLabels("")` nor the `IAssistedBalanceAdapter` marker discriminates — check the ERC-165 bit.

**The whole meta family implements it.** A meta-adapter's shape set *is* its governed instance set, so `positionIds()` reads `getMetaInstances(address(this))` off the NAV Calculator and maps each coordinate through the same internal hook that builds the emitted `positionId`. Enumeration therefore tracks governance with **no adapter state and no admin surface** — adding an instance is already a `MANAGER` config transaction — and the adapters stay immutable. A coordinate that cannot be resolved is dropped and the remaining shapes still returned, so one bad instance never blanks the enumeration.

{% hint style="warning" %}
**The trap is identity, not labelling.** Where an adapter's instance coordinate differs from the `positionId` it emits, enumeration must advertise the **emitted** id. [Aave V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/aave-v3) is the live case: its coordinate is a `ProtocolDataProvider`, while its `positionId` is the `Pool` resolved from it — so it advertises the Pool. Labelling this wrong is invisible, because `positionLabels(dataProvider)` and `positionLabels(pool)` return the *same* `["Market", <marketId>]` (both resolve the same addresses provider). Only the byte-exact join fails, and it fails silently, with both sides looking correctly labelled.
{% endhint %}

So a consumer sees three answers, and the ERC-165 bit tells them apart: an **enumerable** adapter returns its shapes from `positionIds()`; a **single-shape** adapter answers the account-less `positionLabels("")` probe directly; and a **per-account** adapter returns an empty array, meaning its shapes only exist once an owner has fed coordinates.

There is no NAV Calculator passthrough for `positionIds()`: a consumer takes the adapter set from `getAllAdapters()` and staticcalls each adapter directly. That is safe here because a position **shape** is not overridable — nothing on the registry can change what `positionIds()` returns.

**Do not extend that pattern to the display surface.** `protocolName()` and `positionLabels()` *do* have NAV passthroughs — the verbose reads on the position path, and `NAVCalculator.getAdapterDisplayInfo(adapters)` on the configuration path — and those are the only reads that apply the registry's [adapter label overrides](/funds/infrastructure/onchain-accounting/concepts/asset-classification#adapter-label-overrides). A consumer that staticcalls an adapter directly for its name or labels reads the compiled-in value and never learns an override exists.

### Label catalogue

The breadcrumb each adapter returns from `positionLabels`, keyed by `protocolSubId` (the full rendered breadcrumb is `[protocolName, ...labels]`). Leaf placeholders (`<…>`) are read live on-chain; a read that fails **drops only the leaf**, collapsing to the bare category (e.g. `["Vault"]`, `["Market"]`, `["AMM Liquidity Pool"]`).

That degradation is a guarantee, not best effort: `positionLabels` is display-only and **never reverts**. The metadata readers behind it guard the call target's codesize and validate the ABI head before decoding, rather than relying on `try/catch` — which catches neither a call to a code-less address nor a return-data decode failure, since both revert in the *calling* frame. This matters because the NAV Calculator collapses a reverting labels read into an **empty** array, losing the category as well as the leaf, so the row would render with no breadcrumb at all — strictly worse than a missing leaf.

A **malformed `positionId`** is handled the same fail-open way rather than reverting: an id that is not exactly one 32-byte word returns an empty array, and a word that is not a valid address (non-zero upper 96 bits) degrades to the bare category. The accept window is exactly 32 bytes, not "at least 32" — `abi.decode` silently ignores trailing bytes, so a looser guard would let a foreign multi-word coordinate through and label it from its first word, producing a confident, wrong breadcrumb for a position the adapter never emitted.

Deriving a leaf can also be **capped on gas**. Some leaves are built by reaching further into the chain from the governed coordinate — pool coins, a gauge's LP token, an ERC-20 `symbol()` per token — so those reads sit behind a fixed per-leaf ceiling; the rest need none, because they bottom out in the shared metadata readers, which are already capped per read. A leaf that does not fit its ceiling is **dropped, degrading to the bare category** — the same answer an unreadable symbol has always produced.

{% hint style="info" %}
This is a **liveness** bound, not a correctness one, and it applies only to the display path. Valuation reads are untouched: nothing here can change an amount or a price, and a label that cannot be derived never suppresses a position. Bounding the *valuation* reads the same way would be a different and riskier trade — too tight a bound there would skip a real market rather than a display string.
{% endhint %}

| `protocolSubId`                                              | `positionLabels`                                                                                                                                  |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `wallet`                                                     | `["<symbol>"]` (or `["native"]` for the native token)                                                                                             |
| `aave-v3-lending`                                            | `["Market", "<market id>"]`                                                                                                                       |
| `spark-lend`                                                 | `["Market", "SparkLend"]`                                                                                                                         |
| `aave-v3-safety-module`                                      | `["Safety Module"]`                                                                                                                               |
| `aave-v3-umbrella`                                           | `["Umbrella Module"]`                                                                                                                             |
| `morpho-markets`                                             | `["Market", "<collateral>/<loan>"]`                                                                                                               |
| `morpho-vaults` · `euler-vaults`                             | `["Vault", "<vault name>"]`                                                                                                                       |
| `fluid-vaults`                                               | `["Vault", "<collateral>/<debt> #<vaultId>"]` (e.g. `wstETH/USDC #4`) — a Fluid vault is not an ERC20, and the pair alone is not unique           |
| `stakewise-v3-vaults`                                        | `["Vault", "<vault name>"]`, falling back to `["Vault", "<underlying symbol> <vault address>"]` — only the `…Erc20Vault` variants answer `name()` |
| `fluid-ftokens`                                              | `["Lending", "<fToken symbol>"]`                                                                                                                  |
| `compound-v3-comets`                                         | `["Market", "<comet symbol>"]` (e.g. `cUSDCv3`)                                                                                                   |
| `gearbox-v3-markets`                                         | `["Market", "<market name>"]`                                                                                                                     |
| `gearbox-v3-credit-accounts`                                 | `["Credit Account", "<credit-manager name>"]` (→ underlying asset symbol if name is empty)                                                        |
| `cap`                                                        | `["Stablecoin", "cUSD"]` · `["Savings", "stcUSD"]`                                                                                                |
| `curve-pools` · `balancer-v2-pools` · `balancer-v3-pools`    | `["AMM Liquidity Pool", "<sym0>/<sym1>/…"]`                                                                                                       |
| `uniswap-v2` · `pancakeswap-v2`                              | `["AMM Liquidity Pool", "<sym0>/<sym1>"]`                                                                                                         |
| `uniswap-v3` · `pancakeswap-v3`                              | `["AMM Liquidity Pool", "<sym0>/<sym1> <fee>%"]` (e.g. `USDC/WETH 0.3%`)                                                                          |
| `curve-gauges` · `balancer-v2-gauges` · `balancer-v3-gauges` | `["Staking Gauge", "<sym0>/<sym1>/…"]`                                                                                                            |
| `convex-curve-lp-staking`                                    | `["Staking", "Curve LP", "<coins>"]`                                                                                                              |
| `nexus-mutual-staking-pools`                                 | `["Staking Pools", "Pool #<poolId>"]`                                                                                                             |
| `lido-withdrawal`                                            | `["Withdrawal Queue", "stETH"]`                                                                                                                   |
| `stader-withdrawal`                                          | `["Withdrawal Queue", "ETHx"]`                                                                                                                    |
| `kelp-withdrawal`                                            | `["Withdrawal Queue", "rsETH"]`                                                                                                                   |
| `etherfi-withdrawal`                                         | `["Withdrawal Queue", "eETH"]`                                                                                                                    |
| `stakewise-v3-exit`                                          | `["Exit Queue", "<receiptSymbol>"]` (e.g. `osETH`; falls back to `["Exit Queue"]`)                                                                |
| `ethena-cooldown`                                            | `["Unstaking Cooldown", "sUSDe"]`                                                                                                                 |

{% hint style="warning" %}
Labels are **display-only** — never use them for identity, indexing, or reconciliation. Use the typed key above.
{% endhint %}


# Stale prices & sequencer

How the NAV Calculator flags unreliable readings: stale feeds, quote-asset fallback, and the L2 sequencer check.

How the NAV Calculator signals that a reading is unreliable — stale price feeds, an unhealthy quote asset, or a down L2 sequencer (hard signals that must **block** pricing), plus a soft **divergence** signal that flags a price the primary and monitor feeds disagree on. A reading carrying any hard signal must not be used for subscription or redemption pricing.

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

***

## Stale prices and sequencer status

NAV v2 does not collapse staleness into a single flag. Instead:

* `NAV.stalePriceAssets` — assets whose **primary** feed was stale at read time. An empty array means every priced asset had a fresh primary. A registered asset that has a live position but **no usable price feed** reaching the NAV summation is treated the same way: it is priced as 0, marked stale, surfaced here, and summed as a zero-value leg — rather than reverting the entire NAV/positions read. (The explicit query paths — `getPriceData` / `calculateValue` — still revert `PriceFeedNotSet` for a feedless asset, so misconfiguration stays visible to tooling.)
* `NAV.sequencerDown` — `true` if the L2 sequencer uptime feed is stale or within its grace period (L2 chains only; always `false` on Mainnet).
* `NAV.quoteAssetStale` — `true` if a non-USD quote asset's own feed was stale; `value` then falls back to USD even though `quoteAsset` is non-zero. Callers must check this flag before treating `value` as denominated in the requested currency. The same signal is surfaced beyond the `NAV` struct: each `Position` carries a `quoteAssetStale` bool, and `calculateValue` returns it alongside `value` — so a stale-quote USD fallback is flagged on those paths too, not only on the NAV reading.

{% hint style="warning" %}
If `stalePriceAssets.length > 0`, `sequencerDown == true`, or `quoteAssetStale == true`, the NAV reading should **not** be used for subscription or redemption pricing. Use `healthCheck()` to poll chain health without computing a full NAV.
{% endhint %}

***

## Price divergence (the `irregular` signal)

Alongside the hard signals above, the NAV Calculator emits a **soft** divergence signal per asset. Each asset is priced by a single [primary feed](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors); any additional trustworthy source is registered as a **monitor** feed that is read only to check the primary, never to price NAV.

* `NAV.irregularPriceAssets` — assets whose primary price diverged from the worst-case (furthest) healthy monitor beyond the asset's `divergenceToleranceBps`, **or** (USD-pegged stablecoins) strayed from `$1` beyond its `pegToleranceBps`. Each `Position` carries an `irregular` bool, `getPriceData` exposes `irregular` / `divergenceBps` / `monitorFeedCount`, and `getPriceDivergence` returns the full read (primary, monitor median, worst-case bps, direction).
* The check is **suppressed while the primary is stale** — a stale primary is already reported via `stalePriceAssets`.

{% hint style="info" %}
`irregular` is a **quality signal, not a validity flag**: `value` is still populated from the primary feed and the NAV Calculator **never reverts** on divergence. Unlike the hard signals, a non-empty `irregularPriceAssets` does not by itself invalidate the reading — it tells the offchain subscription/redemption processor to investigate, widen its band, or alert before the divergence grows into a stale/blocking condition. See [Price feeds](/funds/infrastructure/onchain-accounting/price-feeds#the-divergence-and-irregular-signal) for the tolerances in use.
{% endhint %}

***

## When no monitor answered (`monitorsUnhealthyPriceAssets`)

A clean `irregular` flag can mean two very different things: the primary was **checked and agreed with its monitors**, or it was **checked by nothing that answered**. The divergence computation is guarded on there being at least one healthy monitor, so when every monitor for an asset is stale, reverting, or reporting a non-positive price, the arithmetic never runs — `divergenceBps` is `0` and `irregular` is `false` *for want of data, not for want of divergence*. From outside, the two cases were the same bytes.

* `NAV.monitorsUnhealthyPriceAssets` — assets that **have monitor feeds configured but where none was readable** at query time. For these, absence from `irregularPriceAssets` carries no information.

Three boundaries matter, because each one is a plausible misreading:

* **It is not the "no monitors configured" case.** That is static configuration, not a runtime failure, and it is deliberately excluded — an asset with nothing configured never appears here however long it goes unchecked. Derive that case off-chain from **`getMonitorFeedCount(asset) == 0` alone**. Do *not* additionally require `divergenceToleranceBps == 0`: that conjunction is unsatisfiable for any asset that ever had a monitor, because setting a tolerance to `0` is rejected while removing a monitor leaves the tolerance untouched. An AND-recipe would silently never flag an asset whose monitors were all removed.
* **Assets whose own primary is unusable are excluded** — stale feed, sequencer down, or a primary whose 8-decimal normalisation degenerates to zero. In each of those the check was suppressed by the *primary*, `stalePriceAssets` already reports the staleness cases, and naming the monitors would point an operator at the wrong contract.
* **It flags the loss of the divergence check, not of all checks.** The `$1` peg guard is monitor-independent, so a stablecoin with a non-zero `pegToleranceBps` can appear here and still be peg-checked.

{% hint style="warning" %}
**Scope: assets the account holds positions in**, exactly like `stalePriceAssets` and `irregularPriceAssets`. The array is built from the assets the account's positions touched, not from the whole registry, so an empty array means "monitors healthy **or** no position held". Reconciling it against the registry-wide per-asset view (`getPriceDivergence(asset).monitorFeedCount`) will show differences that are this scoping rather than a defect.

Two related surfaces deliberately do **not** carry the signal, because `NAVCalculator` is close enough to the EIP-170 contract-size limit that widening them was not affordable: `healthCheck()` — which *does* iterate the whole registry — and `Position.irregular` on `getAccountPositions`, which retains the original ambiguity with no `NAV` struct alongside to join against. Use `getAccountNavVerbose`, whose `VerboseNAV` embeds the `NAV`, when you need positions and the third array together.
{% endhint %}

This is not a rare path. Most monitored assets carry **exactly one** monitor, and many of those have no peg backstop either — so for them "every monitor dead" means *one feed drifting past its heartbeat*, an ordinary event rather than a correlated outage. It is also the shape a provider sunsetting a feed produces: no primary on any chain uses a monitor-only provider, so a sunset degrades the cross-check to "checked by nothing that answered" — visible here — rather than moving the NAV.

***

## L2 sequencer uptime check

On Layer 2 chains (Arbitrum, Base, Optimism, Gnosis), the NAV Calculator additionally checks the **Chainlink L2 Sequencer Uptime Feed**. If the sequencer is down or within its grace period after recovery, `NAV.sequencerDown` is `true` and the entire chain's NAV reading should not be used for pricing.

```solidity
function checkL2SequencerUptime() external view returns (bool stale);
```

Example (illustrative):

```
checkL2SequencerUptime()   → false    // Arbitrum/Base/etc.: sequencer up
checkL2SequencerUptime()   → true     // sequencer down or still in grace period
checkL2SequencerUptime()   → false    // Ethereum mainnet: no uptime feed, always false

healthCheck()
→ (false, [], [])          // healthy: sequencer up, no stale assets, no irregular assets
→ (true,  [], [])          // L2 sequencer down or in grace period
→ (false, [], [0xae78…86CA]) // sequencer up, but an asset's primary diverged from its monitors
```

{% hint style="info" %}
`NAV.sequencerDown` is always `false` on Ethereum Mainnet — there is no sequencer feed to check. The uptime feed and grace period are configured by the admin (`setChainlinkL2SequencerUptimeFeed`, `setChainlinkL2SequencerGracePeriod`).
{% endhint %}

***

## Related

* [NAV Calculator](/funds/infrastructure/onchain-accounting/contracts/nav-calculator) — the `NAV` struct fields and `healthCheck()`.
* [Price feeds](/funds/infrastructure/onchain-accounting/price-feeds) — primary feed, divergence monitors, and per-feed staleness rules.


# Asset classification

How a registered asset is classified on two orthogonal axes — what its price tracks, and how its value accrues.

Every registered asset is classified on **two orthogonal axes**, kept separate because no single category answers both questions:

* **`PegKind`** — what the price *tracks*. `Pegged` or `Floating`.
* **`AccrualKind`** — how value *accrues*. `None`, `Rebasing`, or `Appreciating`.

{% hint style="warning" %}
**The registry lives at `0xf60467a88C2b4d0f46C0c41C3Ba0C74da3d3CCD0`, the same address on all 24 chains.** Every chain the NAV Calculator runs on has one, and each NAV points at it — call `assetKindRegistry()` on the calculator to confirm the binding from the chain itself.
{% endhint %}

**Source:** [`AssetKindRegistry.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/nav/AssetKindRegistry.sol) · [`AssetKind.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/utils/AssetKind.sol)

***

## The two axes

`PegKind` means pegged **to the quote asset** — USD for the default `quoteAsset == address(0)` read — not "pegged to something in general". So USDC, USDT, DAI and GHO are `Pegged`, while **wstETH is `Floating`** even though it is rigidly related to stETH: it does not track USD.

| `AccrualKind`  | What grows                                  | Examples                                   |
| -------------- | ------------------------------------------- | ------------------------------------------ |
| `None`         | Neither balance nor rate                    | WETH, USDC, AAVE, WBTC                     |
| `Rebasing`     | The **balance** grows; unit price stays put | stETH, eETH, aTokens                       |
| `Appreciating` | The **rate** grows; balance stays static    | wstETH, sDAI, sGHO, sUSDe, ERC-4626 shares |

Every registered asset carries an accrual classification, across the seven chains that configure assets. **The live breakdown is not reproduced here.** Read it from the chain — `getAssetKinds(assets)` on the registry — or from the per-chain `assets-and-price-feeds` config, which is what the deploy registers from.

A copied figure is true only of the commit it was copied from, and this one was wrong across four consecutive merges before anyone noticed: every asset registered moves it, and nothing fails when it goes stale.

### Why two axes rather than one category

A single flat enum forces a wrong answer on assets already registered here, because the two properties genuinely cross:

|                | `None` | `Rebasing`                                 | `Appreciating`                     |
| -------------- | ------ | ------------------------------------------ | ---------------------------------- |
| **`Pegged`**   | USDC   | *(an aToken — none registered; see below)* | *(cannot be asserted — see below)* |
| **`Floating`** | WETH   | stETH                                      | wstETH, sDAI, sGHO, sUSDe, sUSDS   |

stETH and wstETH differ on the accrual axis **only** — both are `Floating`. Collapse the axes and that distinction is unexpressible.

{% hint style="info" %}
**`sDAI` is `Floating`, not `Pegged`** — a yield-bearing wrapper has no peg. Its USD price is not `$1` and rises with accrued yield, for the same reason wstETH is `Floating`. `Appreciating` is in fact the mechanism that makes an asset unpeggable: a growing redemption rate on a static balance moves the price off `$1`.
{% endhint %}

***

## The peg axis is derived, not stored

`PegKind` is **not stored anywhere**. It is computed on read from the NAV's per-asset `pegToleranceBps` — the switch that enables the [`$1` peg guard](/funds/infrastructure/onchain-accounting/price-feeds#the-divergence-and-irregular-signal):

```
pegKind(asset) = pegToleranceBps(asset) > 0 ? Pegged : Floating
```

There is exactly **one authority** for whether an asset is pegged, so there is no second copy to fall out of step and no cross-check needed to police one. Whether any given asset reads `Pegged` follows from its own `pegToleranceBps` — read that per asset rather than from a count here.

Because the value is read live rather than snapshotted at classification time, retuning a tolerance is immediately reflected.

{% hint style="warning" %}
**`pegToleranceBps` now does two jobs — read this before zeroing one.** It is both a *threshold* ("flag drift beyond this") and a *switch* ("this asset should be worth `$1`"). Retuning a non-zero threshold — say `100` → `250` — leaves the classification untouched; **it is crossing zero that flips it.** The two jobs are independent everywhere except at that boundary.

The trap lives exactly there: zeroing the tolerance on a genuine stablecoin for an *operational* reason — a feed too noisy to guard, say — silently reclassifies that asset as `Floating`. Nothing reverts and nothing warns, and every off-chain consumer stops believing it is a stablecoin. Disabling the `$1` guard on an asset that really is pegged needs its own field, not a zeroed tolerance.
{% endhint %}

### The two empty cells are empty for different reasons

* **`Pegged + Rebasing` is real and reachable.** An aToken for USDC has a growing balance at a `$1` price. No registered asset occupies the cell, which makes it a forward-compatibility slot rather than a contradiction.
* **`Pegged + Appreciating` cannot be&#x20;*****asserted*****.** Nothing writes a peg kind, so it cannot be declared for an asset. It remains **reachable as a read**: set a non-zero peg tolerance on an asset stored as `Appreciating` and the pair reads back `Pegged + Appreciating`, because the peg is computed from that tolerance. That is a true report of the configuration, not an inconsistency to be caught.

{% hint style="info" %}
**Do not use `PegKind` as a staleness or safety oracle.** It reports what an asset's price is *supposed* to track. It says nothing about whether the current price is fresh, or whether the asset is holding its peg right now — those are [`stalePriceAssets` and the `irregular` signal](/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer).
{% endhint %}

***

## Reading an asset's description

**One call on the NAV Calculator returns everything public about an asset** — both classification axes and its [display labels](#display-labels). A consumer needs only the NAV address it already holds, and never has to learn that a registry exists:

```solidity
// On NAVCalculator
function getAssetInfo(address asset)
    external view returns (PegKind, AccrualKind, string[] memory labels);
```

It returns the enums rather than raw `uint8`s, so callers get a typed ABI rather than two integers to interpret.

{% hint style="info" %}
**`getAssetInfo` is declared on `NAVCalculator` itself, not on `INAVCalculator`.** Bindings generated from the interface alone will not carry it. Generate against the concrete contract, or add the one signature by hand.
{% endhint %}

**The narrower reads, the batch form and the probe live on the registry**, which the NAV names via `assetKindRegistry()`:

```solidity
// On AssetKindRegistry
function getAssetKind(address asset) external view returns (PegKind, AccrualKind);
function getAssetLabels(address asset) external view returns (string[] memory labels);

function getAssetKinds(address[] calldata assets)
    external view returns (PegKind[] memory pegKinds, AccrualKind[] memory accrualKinds);

function isClassified(address asset) external view returns (bool);
```

### What a caller actually receives

The two halves of `getAssetInfo` fail differently, and the difference is deliberate:

* **The classification half fails closed.** An unclassified asset **reverts** `AssetNotClassified` rather than reporting a default. The zero value of `AccrualKind` is `None`, which is a *real* classification and the one most registered assets hold — so a defaulting reply would be indistinguishable from a configured one. Probe with `isClassified` when an unclassified asset is an expected state.
* **The labels half never fails.** `getAssetLabels` does not revert; an unlabelled asset returns an **empty array**, which is a real answer rather than a gap.

That asymmetry is the whole rule: there is no defensible default classification, but "no labels" *is* the honest answer.

{% hint style="warning" %}
**A NAV that is not wired to a registry reverts rather than answering.** This is a different failure from an unclassified asset, and it looks nothing like an empty result: a consumer gets a revert, not a blank. Returning `(Pegged, None)` instead would be indistinguishable from a configured answer for a real stablecoin.

Read `assetKindRegistry()` to see which registry — if any — a given NAV points at. Wiring is a separate step from deploying either contract, so a NAV can be entirely correct while every classification read reverts.
{% endhint %}

***

## Display labels

Alongside the two axes, an asset carries an ordered list of **display labels** — free text for presentation, which nothing on-chain parses.

**Order is significant, and the list is not a set.** Index 0 is the protocol or issuer; later indices narrow to the product or kind:

```
["Aave V3", "Savings"]
["Lido", "LST"]
```

That broad-to-narrow **order** is the same one `protocolName()` and `positionLabels()` compose in — but the **content** differs, and the difference matters when you are writing the array. Asset labels carry the protocol at **index 0**. [Position labels](/funds/infrastructure/onchain-accounting/concepts/position-identity#human-readable-labels) never do: there, `protocolName()` supplies the protocol separately and `positionLabels()` starts at the product (`["Market", "wstETH/USDC"]`, `["Withdrawal Queue", "stETH"]`).

So the two are alike in ordering and opposite in what index 0 holds. Reading either array as an unordered set discards the only structure it has.

At most **4** labels, each at most **32 bytes**, none empty — `TooManyAssetLabels`, `AssetLabelTooLong` and `EmptyAssetLabel` reject the rest at write time. **Zero labels is legitimate**, not a misconfiguration.

**A wrong label can be corrected in place**, without touching the asset's registration or its price feeds:

```solidity
// On AssetKindRegistry — not on the NAV
function setAssetLabels(address asset, string[] calldata labels) external onlyNavManager;
```

It **replaces** the whole array rather than editing an element, and it can revert six ways. An integrator decoding errors needs to handle all six — the role gate, two of its own, and three content bounds it shares with `registerAsset`:

| revert                        | when                                                                                                |
| ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `NotAuthorized`               | the caller does not hold the NAV's `MANAGER` role — checked first, so the likeliest one in practice |
| `AssetNotRegisteredForLabels` | the asset is not registered on the NAV                                                              |
| `EmptyAssetLabelSet`          | the array is empty                                                                                  |
| `TooManyAssetLabels`          | more than 4 labels                                                                                  |
| `AssetLabelTooLong`           | an element exceeds 32 bytes                                                                         |
| `EmptyAssetLabel`             | an element is the empty string                                                                      |

{% hint style="info" %}
**`setAssetLabels` replaces the whole list; it cannot empty one.** It rejects an empty array, so through this call the transitions are *none → some* and *some → other* only. Labels do still go from *some → none* — `unregisterAsset` clears them — but that ends the registration, which is not a label operation.

The **per-element** bounds are identical on both write paths — at most 4 labels, 32 bytes each, no empty element — so a label list that could not be registered cannot be set either. They differ in two places: the empty *array*, which `registerAsset` accepts (meaning "leave labels alone") and `setAssetLabels` rejects; and their preconditions, which are opposites — `registerAsset` requires the asset to be **absent** from the NAV, `setAssetLabels` requires it to be **present**. They are not interchangeable.

An asset can also be unlabelled because it never went through `registerAsset`: the chain's **native asset** is registered during initialization rather than by that call, so in practice `setAssetLabels` is how it gains labels.
{% endhint %}

{% hint style="danger" %}
**Never unregister an asset to fix its display strings.** `unregisterAsset` is not a label operation. It tears down the asset's entire pricing configuration — every price feed, every monitor feed, both tolerances — and takes the asset out of the NAV until it is registered again, so every consumer's NAV loses its balance in the meantime.

Re-registering does **not** restore that configuration. It sets one primary feed; everything else is rebuilt by hand, in an order that matters, and `pegToleranceBps` returning to 0 silently flips a stablecoin's [derived peg classification](#the-peg-axis-is-derived-not-stored) to `Floating`. Treat unregistration as removing the asset from the portfolio and re-onboarding it from scratch — see [Price feeds](/funds/infrastructure/onchain-accounting/price-feeds) for what that entails — not as an edit.

Correcting a wrong label requires none of it. That is what `setAssetLabels` is for.
{% endhint %}

***

## Adapter label overrides

[Adapter labels](/funds/infrastructure/onchain-accounting/concepts/position-identity#human-readable-labels) are produced by adapter code — `protocolName()` for the protocol, `positionLabels(positionId)` for the breadcrumb beneath it — so a wrong one is wrong for **every position that adapter serves**, not for one row. Redeploying the adapter is one way to fix that. It is no longer the only way.

Two per-adapter overrides are stored on the registry and applied at read time. All four calls are gated on the NAV's `MANAGER` role; the two **setters** additionally require the adapter to be registered, while the two **clearers** deliberately do not — so an override can still be removed after its adapter has been deregistered:

```solidity
// On AssetKindRegistry
function setAdapterProtocolName(address adapter, string calldata name) external;
function clearAdapterProtocolName(address adapter) external;

function setAdapterLabelCategory(address adapter, string calldata category) external;
function clearAdapterLabelCategory(address adapter) external;
```

An override that is unset falls through to whatever the adapter itself returns, so this changes presentation without touching the adapter. Setting one on an unregistered adapter reverts `AdapterNotRegistered`; an empty string reverts `EmptyAdapterOverride` — use the matching `clear…` call to remove one — and an over-long value reverts `AdapterOverrideTooLong`. **The clearers are not idempotent:** clearing an override that was never set reverts `AdapterOverrideNotSet`, so a cleanup batch run over several adapters fails on the first one that carried nothing.

**What the two overrides do not reach is any segment&#x20;*****beneath*****&#x20;index 0.** The category override replaces index 0 and nothing else; the computed leaf below it — which many adapters derive live from on-chain state, per position — is left untouched and still read live on every call. Correcting a leaf still means changing whatever produces it, the adapter code or its constructor argument, and redeploying that adapter. That is a **current limitation of the override surface**, stated as such: it is not a claim about what the surface was intended to be.

{% hint style="info" %}
**Both read paths agree.** The overrides apply on the **position** path — the verbose reads that render a position's breadcrumb — and on the **configuration** path, `NAVCalculator.getAdapterDisplayInfo(adapters)`, which returns each adapter's effective name and its positionId-independent breadcrumb. Pair that with `getAllAdapters()` to group configured adapters by exactly the string the position path will report.

**Read through the NAV Calculator, not the adapter.** A consumer that `staticcall`s `protocolName()` on an adapter directly reads the compiled-in value and never learns an override exists.

**Reading through the NAV is necessary but not sufficient.** The registry lookup is **fail-open**: if the registry is unwired, is an EOA, reverts, or answers with something unreadable, the read degrades silently to the adapter's own compiled-in name rather than failing. Replacing the registry drops every override the same way. So a correct consumer can still be served a stale name — the override surface is a presentation layer, not a guarantee.
{% endhint %}

{% hint style="warning" %}
**The override is keyed by&#x20;*****adapter*****, so it is only meaningful when index 0 is a literal&#x20;*****and*****&#x20;the same literal for every position that adapter emits.** Both halves are required, and a different adapter breaks each one.

For most adapters index 0 is exactly that — one fixed category, with the computed leaf at index 1 untouched and still read live. **One deployable adapter is excluded:**

* **`ERC20DefaultBalanceAdapter`** — index 0 is not a category at all. Its reply is a **single element**, which for an ERC-20 is a live `symbol()` read and for the native asset is the literal `"native"`. **An override replaces that element in either case** — the native leg is *not* exempt, because the replacement is applied whenever the array is non-empty, without inspecting what index 0 held.

**The exclusion is not enforced and not detectable.** The mechanism sees an array, not the code that produced it. It is an operational rule, asserted as a documented hazard in [`AdapterLabelOverrides.t.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/test/nav/AdapterLabelOverrides.t.sol).

{% hint style="success" %}
**Every adapter in `src/balances/` emits a per-adapter constant at index 0, so per-adapter label overrides are safe on all of them.**

The shape that would break this is an index-0 literal that varies *by position* — a per-adapter table cannot express a per-position value, so one override would rewrite every leg and correcting one would corrupt another. Cap is the case that would otherwise have had it, and is split into [`CapStablecoinBalanceAdapter`](/funds/infrastructure/onchain-accounting/balance-adapters/cap-stablecoin) and [`CapSavingsBalanceAdapter`](/funds/infrastructure/onchain-accounting/balance-adapters/cap-savings) so each emits one constant.

`AdapterLabelOverrides.t.sol` pins the limitation against a mock with no counterpart in `src/`, deliberately: the constraint is real and unenforceable by the compiler, so an author reaching for a per-position literal meets it as a failing test rather than rediscovering it.
{% endhint %}

**Before setting a category override, check the adapter against the notes on `setAdapterLabelCategory` in** [**`IAssetKindRegistry`**](https://github.com/karpatkey/onchain-accounting/blob/main/src/nav/IAssetKindRegistry.sol) — that is where the exclusions are maintained, and it is the record. Do not re-derive the rule from one adapter's labels: the condition is per-position stability, which a single position cannot show you.
{% endhint %}

{% hint style="info" %}
**What the override lookup costs on a verbose read.**

The figures are computed by [`test/nav/AdapterLabelOverrides.t.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/test/nav/AdapterLabelOverrides.t.sol), which measures this surcharge warm and cold and prints each number it derives. Run it for current values rather than budgeting from a figure quoted here — the harness is revised as the read paths are, and a number copied out of it is only true of the commit it was copied from. What that suite *enforces* is stated below, and each of these is an assertion: a change that breaks one breaks the build rather than silently ageing this page.

* **A miss is dearer than a hit, and every adapter is a miss today.** A hit skips the adapter's `protocolName()` staticcall; a miss pays the registry lookup **and still makes the adapter call**, so it is strictly the larger piece of work. No override has been set on any adapter, so the miss is what the system pays now. That saving is **specific to the protocol-name override** — it comes from skipping the adapter call. The category lookup is read on every verbose call whether or not one is set, so setting a category override does not make the read cheaper.
* **The surcharge scales with distinct adapters, never with positions.** Two adapters across three positions cost the same as two across two; the third position adds nothing. Budget by the number of distinct adapters a read touches, never by row count. Each bound defending this was set from a mutant that breaks it, not from taste.
* **Cold exceeds warm, and warm is a floor.** A warm measurement touches the slot before reading it; a real portfolio reads each adapter's slot cold the first time it touches it.

For an order-of-magnitude at operator scale, the cold account path over mainnet's registered adapter set costs roughly **9.5k gas per emitting adapter** — the shape a real portfolio has, rather than a single-adapter microbenchmark. Two caveats travel with that number and must not be separated from it: `vm.cool` restores cold **slots** but not the cold-**account** premium, so even the cold figures are a floor on what a fresh call pays; and the configuration surface's per-entry figure is measured with repeated addresses that are warm after the first touch, so it is a warm floor and must never be quoted as a per-distinct-adapter cost.
{% endhint %}

**The facade's `unregisterAsset` drops an asset's labels; `clearAssetKind` keeps them.** The two look interchangeable and are not:

* `clearAssetKind` ends the *classification* while the asset stays registered on the NAV — its labels still describe a live asset, so they are kept.
* `AssetKindRegistry.unregisterAsset` ends the *registration*, so it deletes the labels outright — after **this** call, and only this one, a later re-registration starts from none.

{% hint style="warning" %}
**That holds for the facade's `unregisterAsset`, not for the NAV's.** A `MANAGER` calling `NAVCalculator.unregisterAsset` directly — permitted by design, the same [two doors](#governance) as registration — **strands the labels**: the registry still holds them, and the facade's `unregisterAsset` can no longer clear them because it reverts inside the NAV, which no longer has the asset.

**The classification survives too**, and this is the part that misleads: `isClassified` still returns `true` and `getAssetInfo` still *answers* rather than reverting, so a consumer sees a live, classified, plausibly-labelled asset that the NAV holds nothing for. Unregistration also cleared the asset's tolerances, so if it *had* a peg tolerance the derived peg now reads `Floating` — but most assets never had one, so an unchanged `Floating` is not evidence that nothing happened.

**Getting out.** Re-register through the facade **with the correct labels**: given a non-empty array, `registerAsset` overwrites whatever is there, deliberately, since registration is what defines an asset's labels. One transaction. Re-registering with an *empty* array does not clear the stale set — an empty list means "leave labels alone" — and if that has already happened, `setAssetLabels` replaces them.

The two calls have **opposite preconditions**, which is easy to get backwards: `registerAsset` requires the asset to be **absent** from the NAV and reverts `InvalidArguments` if it is already registered, while `setAssetLabels` requires it to be **present** and reverts `AssetNotRegisteredForLabels` if it is not.

Re-registration fixes the labels, not the pricing. It restores one primary feed and leaves the tolerances and every monitor feed cleared — treat that as the [re-onboarding](#display-labels) it is.
{% endhint %}

***

## Governance

Registering an asset, classifying it and labelling it are **one operation**:

```solidity
// On AssetKindRegistry — the NAV has a registerAsset too, with a different signature
function registerAsset(
    address asset,
    address priceFeed,
    IPrices.PriceType priceType,
    uint256 chainlinkHeartbeat,
    AccrualKind accrualKind,
    string[] calldata labels
) external;
```

`AssetKindRegistry.registerAsset` stores the accrual kind and the labels, then forwards to the NAV's own `registerAsset` in the same transaction, so if either half reverts the whole transaction reverts and neither takes effect. An asset therefore cannot become registered-but-unclassified through a partly-applied change. Unregistration works the same way, and additionally drops the labels.

{% hint style="warning" %}
**That guarantee covers the facade, not the NAV.** `NAVCalculator.registerAsset` is still callable directly by any `MANAGER`, and it writes **neither** classification nor labels — so a registered asset having a classification is a property of *which door was used*, not of the system. This is the case where [`getAssetInfo` reverts `AssetNotClassified`](#what-a-caller-actually-receives) for an asset that really is registered and really is being priced.

**Both halves can now be repaired in place, in two transactions** — `setAssetLabels` for the labels, `setAssetKind` for the classification. Neither requires unregistering, and neither touches the asset's price feeds.

The two do not validate alike, though. `setAssetLabels` checks that the asset is registered on the NAV and reverts `AssetNotRegisteredForLabels` if not — so a mistyped address is caught. **`setAssetKind` has no such gate**: it rejects only `address(0)`, and will happily classify an address the NAV has never registered, emitting the event as though it were real. Nothing later reverts to reveal it.

What the facade still buys is **atomicity**, not exclusivity: `registerAsset` makes registration, classification and labels one transaction that cannot half-apply, whereas repairing after the fact leaves a window in which the asset is registered and priced but not yet classified — during which `getAssetInfo` reverts. Unregistering **through the facade** and re-registering remains the only way to *end* a label list, since `setAssetLabels` rejects an empty array — and it carries the [cost described above](#display-labels).

Whether the two doors are narrowed to one is a deployment-time choice about who holds `MANAGER`, not something the contracts settle. Treat "registered" and "classified" as separate questions and probe with `isClassified` when the answer matters.
{% endhint %}

Mutations — `registerAsset`, `unregisterAsset`, `setAssetKind`, `setAssetKinds`, `clearAssetKind`, `setAssetLabels` — are gated on the **NAV's `MANAGER` role**:

```solidity
// On AssetKindRegistry — the onlyNavManager modifier
if (!IAccessControl(NAV).hasRole(MANAGER, msg.sender)) revert NotAuthorized();
```

The registry holds **no roles of its own** and has no separate admin. Authority is exactly the NAV's `MANAGER` set — one set of humans for the whole system, rather than a second contract with its own governance to keep in step. The NAV address is set in the registry's constructor and immutable.

The pointer in the other direction is not immutable: a `MANAGER` aims the NAV at a registry with `setAssetKindRegistry`, which rejects `address(0)`. It is re-settable on purpose — the registry is a plain contract rather than a proxy, so replacing it means deploying a new one and re-pointing the NAV.

{% hint style="warning" %}
**"No roles of its own" is not "unprivileged" — both halves matter.** Because `registerAsset` forwards to the NAV, and the NAV gates that call on *its* caller, the registry itself must hold `MANAGER` on the NAV. So the registry **can register and unregister assets**, and a fault in it reaches the NAV's asset set. What the design removes is a *second role surface to keep in sync*; it does not remove privilege from the system.
{% endhint %}

The stored axis is written from per-asset configuration, where the accepted values are `None`, `Rebasing` and `Appreciating`, case-exact. A pre-deploy check rejects an absent, empty, mis-cased or unrecognised **value** — but it validates the value, **not the field set**: a misspelled *key* (`accrualkind`, `accrual_kind`) is silently ignored and reaches the deploy as if the field were absent.


# Price feeds

How assets are priced — one primary feed per asset, divergence monitors, feed types, and the custom price feeds.

Every asset registered in a NAV Calculator has exactly **one primary price feed** and **zero or more monitor feeds**. The primary feed converts a raw token balance into a value in the quote currency (USD by default, 8 decimals) — it is the **only** feed that prices NAV. Monitor feeds are read **only** to detect divergence; they never price the asset. An asset is marked stale when its **primary** feed is stale (or the L2 sequencer is down).

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

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-676c68ae91fdf24902bbcd3deea29cf3d8142179%2Flegend.svg?alt=media" alt="Diagram key: Input/source is light blue, Transform is violet, Staleness gate is amber, Output is green, Off-chain is dashed grey."><figcaption><p>Diagram key — the same colours are used across every price-feed and adapter diagram.</p></figcaption></figure>

***

## How prices are resolved

When pricing an asset, the NAV Calculator reads its **primary feed** and, in the same call, compares that price against the asset's **monitor feeds** to compute a divergence signal. Consumers can inspect both with `getPriceData`:

```solidity
function getPriceData(address underlyingAsset)
    external view returns (PriceFeedData memory);
```

The key fields of the returned `PriceFeedData`:

| Field              | Description                                                                                                     |
| ------------------ | --------------------------------------------------------------------------------------------------------------- |
| `price`            | Price from the **primary** feed (`0` if the primary is stale)                                                   |
| `decimals`         | Decimal places in `price` (pinned per asset; all feeds for an asset share decimals)                             |
| `stale`            | `true` if the **primary** feed is stale, or the L2 sequencer is down                                            |
| `healthyFeedCount` | How many pricing feeds passed the staleness gates this read (`1` in the intended one-primary configuration)     |
| `irregular`        | `true` if the primary diverged from the monitors beyond tolerance, or (stablecoins) strayed from the $1 peg     |
| `divergenceBps`    | Worst-case deviation between the primary and any healthy monitor, in basis points (`0` when no healthy monitor) |
| `monitorFeedCount` | How many monitor feeds passed the staleness gates this read                                                     |

{% hint style="info" %}
`irregular` is a **pricing-quality signal, not a validity flag**. Even when `irregular` is `true`, `price` is still populated from the primary feed — the NAV Calculator is a read-only view that **never reverts** on divergence. It is the offchain subscription/redemption processor that decides how to react (pause, widen a band, alert). See [Primary feed and divergence monitors](#primary-feed-and-divergence-monitors).
{% endhint %}

The value of a (non-debt) position is:

```
positionValue = (amount × price) / 10^(assetDecimals)
```

normalised into the quote asset's decimals.

Example (illustrative values; `WETH` = `0xC02a…6Cc2`, a Chainlink primary with one Redstone monitor):

```
getPriceData(0xC02a…6Cc2)
→ PriceFeedData {
    priceFeed: 0x5f4e…8419, priceType: 0 /* Chainlink */, price: 392265000000,
    decimals: 8, chainlinkHeartbeat: 3600, updatedAt: 1718901200,
    stale: false, sequencerDown: false, healthyFeedCount: 1,
    irregular: false, divergenceBps: 11, monitorFeedCount: 1
  }
```

***

## Primary feed and divergence monitors

Each asset is priced by exactly **one deterministic primary feed**. Any additional trustworthy source is registered as a **monitor feed** — read on every NAV call to check that the primary is still telling the truth, but **never** used to value the asset.

### Why one primary, not the freshest feed

Pricing from whichever of several redundant feeds is **freshest** tracks market noise instead of redeemable value. When an asset has both a fundamental exchange-rate feed (its redeemable value) and a market composite (Redstone/API3), the market feed updates more often and therefore wins. On a leveraged fund the resulting NAV swings are amplified by gross leverage, with no position change behind them.

One deterministic primary as the sole pricing feed removes that noise, and the redundant sources serve as divergence-only monitors — so a market wobble raises an **alert** instead of moving the share price.

### The divergence and `irregular` signal

On each read, over the asset's healthy monitor feeds (each an independent `<asset>/USD`, 8-decimal feed, gated on its own heartbeat), the NAV Calculator computes:

* **`divergenceBps`** — the **worst-case** (furthest) single-monitor deviation from the primary: `max |primary − monitorᵢ| / primary`, in basis points. Using the furthest monitor (not an average) means two monitors straddling the primary can't mask a genuine one-sided split.
* **`irregular`** — set `true` when `divergenceBps` exceeds the asset's **`divergenceToleranceBps`**, **or** (for USD-pegged stablecoins only) when the primary strays from `$1` beyond the asset's **`pegToleranceBps`**.

The **median** monitor price and a `primaryAboveMonitors` direction flag are exposed separately (via [`getPriceDivergence`](/funds/infrastructure/onchain-accounting/contracts/nav-calculator#read-functions)) for directionality — neither feeds the `irregular` flag itself. Both divergence and peg checks are **suppressed while the primary is stale** (a stale primary is already flagged via `stale`).

A monitor past its own heartbeat **self-excludes** rather than contributing a stale rate to the comparison — and because `monitorFeedCount` reports how many passed, that degradation is observable rather than silent: an asset whose count drops has lost a cross-check, even though `irregular` stays `false`. This is what makes it safe to keep a monitor whose upstream feed may be retired: the worst case is losing the signal, never a wrong one.

Tolerances are **per-asset config**, default `0` (= that check disabled). The values in use:

| Kind                                                                                                                                    | `divergenceToleranceBps` | `pegToleranceBps` |
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----------------- |
| **Fundamental-vs-market** (Custom exchange-rate primary + market monitor) — wstETH, weETH, ezETH, rETH, ETHx, osETH, stETH …            | 250 (2.5%)               | —                 |
| **Market-vs-market** (Chainlink primary + a second market feed) — ETH/WETH, USDe …                                                      | 50 (0.5%)                | —                 |
| **USD-$1 stablecoins, monitored** (peg guard **plus** a market-divergence monitor) — mainnet USDC & USDT, Base USDC …                   | 50                       | 100 (1%)          |
| **USD-$1 stablecoins, peg-only** (no second market feed on that chain, so divergence is disabled) — Base USDT, all Gnosis stablecoins … | 0                        | 100 (1%)          |

A stablecoin's `$1` **peg guard is independent of its divergence monitor**: where a chain has no second market feed for it — e.g. RedStone publishes no USDT/USD on Base, and the Gnosis stablecoins carry no market monitor — the asset runs **peg-only** (`divergenceToleranceBps = 0`, `pegToleranceBps = 100`). It still raises `irregular` when the Chainlink primary strays past `1%` from `$1`, just without a market cross-check.

{% hint style="info" %}
**`pegToleranceBps` is the system's definition of "pegged", not merely a setting on pegged assets.** An asset is pegged exactly when it carries a non-zero `pegToleranceBps`, and that value is readable on-chain per asset — so peggedness is **derived from one authority** rather than recorded a second time somewhere else and kept in step. There is deliberately no separate stored peg flag: a second copy would be able to disagree with this one, and the only defence against that would be a cross-check that exists solely because the copy does.

The practical consequence for a consumer classifying an asset: read the tolerance, do not maintain your own list. A yield-bearing wrapper is not pegged for exactly this reason — its redemption rate carries its price off `$1`, so it holds no peg tolerance, so it does not read as pegged.
{% endhint %}

{% hint style="warning" %}
`irregular` never changes `price` or `value`, and the NAV Calculator **never reverts** on divergence — it is a read-only view. A `true` value tells the offchain [subscription/redemption processor](/funds/infrastructure/onchain-accounting/contracts/nav-calculator) to withhold or widen pricing and alert operators. See [Stale prices & sequencer](/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer) for how it sits alongside `stale`, `sequencerDown`, and `quoteAssetStale`.
{% endhint %}

{% hint style="info" %}
**Transitional fallback.** The primary lives in the asset's `priceFeeds` list (`priceFeeds[0]`); monitors live in a separate `monitorFeeds` list. The legacy freshest-wins selector is retained only for the migration window where an asset might still carry a second **pricing** feed — with the intended one-primary configuration the selector runs once and simply returns the primary. Monitor feeds must report **8 decimals** (`MonitorFeedDecimalsMismatch`); pricing feeds for one asset must share `decimals` (enforced on `addPriceFeed`).
{% endhint %}

***

## Supported feed types

`PriceType` enum:

| Value | Type        | Notes                                                                    |
| ----- | ----------- | ------------------------------------------------------------------------ |
| 0     | `Chainlink` | Chainlink `AggregatorV3Interface` push feed                              |
| 1     | `Redstone`  | Redstone Classic Push — AggregatorV3-compatible, read like Chainlink     |
| 2     | `API3`      | API3 dAPI via `Api3ReaderProxyV1` (18-dec proxy scaled to 8-dec at read) |
| 3     | `Custom`    | A custom `ICustomPriceFeed` adapter with internal staleness logic        |

{% tabs %}
{% tab title="Chainlink / Redstone / API3" %}
AggregatorV3-shaped feeds. Each is gated on a configurable **heartbeat** (`block.timestamp − updatedAt ≥ heartbeat → stale`). Redstone and API3 are read through the same `latestRoundData` path as Chainlink.
{% endtab %}

{% tab title="Custom" %}
Custom composite adapters (`ICustomPriceFeed`) are deployed for assets without a direct USD feed. Each prices its asset as **`rate × base/USD`** — a **fundamental** on-chain exchange rate (e.g. wstETH's `stEthPerToken()`) times the **base asset's** USD price (ETH/USD for the LSTs, a stablecoin/USD for the ERC4626 vaults). These Custom feeds are the intended **primary** for their asset; any market-based source is registered as a [monitor](#primary-feed-and-divergence-monitors), not folded into the price.

* The **`rate` leg** is a live on-chain exchange rate pinned in the adapter (a protocol `getRate()` / `convertToAssets()` / `stEthPerToken()` call), with no market oracle in the pricing path.
* The **`base/USD` leg** is **not** a hardcoded oracle — it is read from the NAV's **own primary feed** for the base asset, via `getPriceDataNoDivergence(baseAsset)`, so the whole system uses one price source per base asset (see [Base/USD from the NAV's own selector](#base-usd-from-the-navs-own-selector)).
* `getLatestPrice()` returns `(price, stale)` and is the **primary** staleness gate, encoding the adapter's own logic (OR of its rate-leg heartbeats **and** the NAV's stale bit for the base asset).
* `latestRoundData()` supplies `updatedAt` for the secondary heartbeat gate.

See the per-asset pages below for each adapter's composition.
{% endtab %}
{% endtabs %}

### Choosing a heartbeat: exactly the provider's declared cadence

**A registered heartbeat MUST equal the provider's declared heartbeat — not more, not less.** It is the line between "this price is usable" and "`$0`, and the read is discarded", and the two ways of being wrong about that line are not symmetric.

{% hint style="danger" %}
**Claiming a price is fresh when it might be stale is the only error this system treats as critical.**

Registering *above* the declared cadence buys a window in which a **dead feed still reads fresh**, and a stale price valued as live puts a wrong number into NAV silently — nothing reverts, nothing flags. Registering *below* it produces the opposite error: a healthy price is declared stale, the asset prices `$0` and the read is discarded.

That second error is a **liveness cost, and it is loud** — the asset shows up in `stalePriceAssets` and a consumer can see it happened. The first is a **correctness error, and it is silent**. Those are not equally bad, and the policy is built on that asymmetry.
{% endhint %}

**Why not simply add margin, then.** Chainlink's heartbeat is a round-**initiation** commitment, not a landing guarantee: a new aggregation round *starts* after the specified time, and the answer is written only once enough oracles respond. So `updatedAt` lands after the declared value by an aggregation, transmission and block-granularity delay — measured across 43 feeds whose declared values span a 339× range, that delay is **+2 s to +96 s and does not scale with the declared cadence**.

The delay is real, and it is what motivated the old `declared + 1 h` convention this replaces. But inside `[declared, declared + X]` a price might be healthy-with-latency or might be a dead feed, `X` is a random variable, and nothing in the data separates the two. **Any margin is a bet that the ambiguity resolves benignly.** Equality refuses the bet.

**The cost, measured before it was accepted.** The staleness share is landing-delay ÷ publish-interval: 0.00–0.03 % of reads across most of the fleet, rising for the fastest feeds — the ones that previously carried the largest false-fresh windows. Those windows were not small: several registrations sat at `86400` against declared values of a few minutes, and one set against a declared **27 s**, so a dead feed would have read fresh for nearly 24 hours.

**The guard errors in both directions.** Preflight compares every registration to the declared cadence on record: above it is flagged as false-fresh exposure, below it as manufactured staleness. Registered values are not reproduced here — read them from the per-chain `assets-and-price-feeds` config, which is what the deploy registers from.

{% hint style="info" %}
This is an **absolute** rule about a feed and its provider, and it composes with the **relative** rule in the next section: a derived feed's heartbeat may never be tighter than its base's. The relative rule is about the same asymmetry one level up — a derived feed's `updatedAt` *is* its base leg's timestamp, so gating it tighter than the base cannot prevent a stale price. It can only manufacture a stale verdict while the base is still fresh.
{% endhint %}

***

## Base/USD from the NAV's own selector

A custom feed prices its asset as **`rate × base/USD`**. The `base/USD` term — **ETH/USD** for the LSTs, a **stablecoin/USD** for the ERC4626 vaults — is read from the NAV Calculator's **own** price selector, `getPriceData(baseAsset)`, rather than from a hardcoded Chainlink oracle embedded in the feed.

**Why.** The NAV prices the base asset itself (e.g. WETH) through its [primary feed](#primary-feed-and-divergence-monitors). If a custom feed embedded its *own* Chainlink ETH/USD while the NAV valued WETH off a different oracle, the two `ETH/USD` terms would **not cancel** in ETH-quoted NAV: a divergence between the two ETH/USD sources could move NAV by `≈ spread × grossLeverage` with **zero position change**. Sourcing the base term from the NAV's own primary makes the cancellation **structural** — the derived asset and the base asset's own leg always use the identical `base/USD` value in the same block.

{% hint style="info" %}
The base term is read via `getPriceDataNoDivergence(baseAsset)` — the base asset's **primary** price, skipping the monitor/divergence computation on this hot path. The base asset keeps its own divergence monitors for alerting; the derived assets simply *follow* the base primary. A derived asset reads **stale** whenever the NAV reports its base asset's primary feed stale (past its heartbeat).

{% hint style="warning" %}
**Never register a derived feed with a heartbeat tighter than its own base asset's.** Because the derived feed inherits the base's staleness verdict regardless, a tighter heartbeat cannot prevent a stale price — it can only **manufacture a stale verdict while the base is still fresh**, zeroing the asset's price and listing it in `stalePriceAssets` for no reason. A single-primary asset has no second feed to rescue that read.

The invariant is guarded pre-deploy. Matching the base is the *floor*, not the whole answer — but the base's own value is no longer a free parameter: under [the rule above](#choosing-a-heartbeat-exactly-the-providers-declared-cadence) it is registered at exactly its provider's declared cadence, so a derived feed that matches its base inherits that same line rather than a padded one.

The **reverse** direction is safe and intentional: several derived feeds sit at `86400` over a WETH base at `3600`. A looser derived heartbeat cannot over-report, precisely because the base's own verdict is inherited either way — so tightening those would only add a needless blackout window.
{% endhint %}

An **L2 sequencer outage does&#x20;*****not*****&#x20;zero the base leg.** `computeNav` prices every *direct*-fed position at its last value during a sequencer outage and surfaces the outage only through the top-level `NAV.sequencerDown` flag, not per position. If custom feeds zeroed on sequencer-down, custom-priced positions would crater to `$0` while direct ones stayed live — an inconsistent, understated NAV. So the base leg is marked stale only on **genuine base-feed staleness**, matching the direct-feed path; the L2 sequencer stays a single top-level signal.
{% endhint %}

{% hint style="warning" %}
**Circularity invariant.** A `baseAsset` (WETH, GNO, the stablecoins) MUST be priced by **direct** feeds (Chainlink / API3 / Redstone) — never by a NAV-sourced custom feed — otherwise `getPriceData(baseAsset)` would recurse. The shared helper is [`NavBaseUsdLib`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/NavBaseUsdLib.sol).

**The one-hop case is rejected on-chain at registration.** Registering a Custom feed makes the NAV probe the feed's `BASE_ASSET()`; if that base *is* the asset being registered, registration reverts `CustomFeedBaseCircular`. A feed that does not answer `BASE_ASSET()` at all — or answers with something that is not a clean address word — is rejected too (`CustomFeedBaseUnreadable`), so the check fails closed rather than waving through what it cannot read. It runs on the same validation path as the [asset-binding check](#custom-price-feeds), so it covers registration, adding a feed, adding a monitor, and the read-only pre-flight query.

**Multi-hop cycles are not caught on-chain, deliberately.** A feed for A based on B and a feed for B based on A both register cleanly, *in either order*: the check is handed only `(asset, feed)` and never reads the registry. That is not an oversight — the only on-chain alternative is an ordering-dependent registry scan, and such a scan **cannot answer "no"**, because a base with no custom feed today can acquire one tomorrow. A check that cannot fail is worse than no check.

Instead the cycle is made **unconstructible in configuration**, and that is now asserted rather than assumed: of the **41** configured Custom `(asset, feed)` pairs across all seven chains — 31 primaries and 10 monitors — **none** has a base priced by a Custom primary. Every base resolves to a Chainlink-only asset: WETH, USDC, GHO, USDS, USDe, GNO, WXDAI. The pre-deploy config check treats a violation as an **error**, not a warning.
{% endhint %}

{% hint style="danger" %}
**A wrong base is worse than a cyclic one, and nothing on-chain catches it.** A cycle at least announces itself in gas — a poisoned read has been measured at \~153× the cost of a healthy one. A base that is merely the **wrong asset** has no such signature: no gas anomaly, no `stale` flag, no `irregular` flag. It returns a plausible, wrong number at an ordinary read.

The registration guard rejects a base that is the asset *itself*; it cannot tell whether a base is the *right* asset. **35 of the 45 pairs depend on the configured base being correct.** The exception is the ten ERC-4626 vault-rate feeds — [sUSDe](/funds/infrastructure/onchain-accounting/price-feeds/susde), [sUSDS](/funds/infrastructure/onchain-accounting/price-feeds/susds), [syrupUSDC](/funds/infrastructure/onchain-accounting/price-feeds/syrupusdc), [RWIV](/funds/infrastructure/onchain-accounting/price-feeds/rwiv), [sGHO](/funds/infrastructure/onchain-accounting/price-feeds/sgho), [sXDAI](/funds/infrastructure/onchain-accounting/price-feeds/sxdai), [spUSDC](/funds/infrastructure/onchain-accounting/price-feeds/spusdc), [spUSDT](/funds/infrastructure/onchain-accounting/price-feeds/spusdt), [spETH](/funds/infrastructure/onchain-accounting/price-feeds/speth), [sDAI](/funds/infrastructure/onchain-accounting/price-feeds/sdai) — whose constructor pins the base to `IERC4626(vault).asset()` and reverts `BaseAssetMismatch` on anything else.
{% endhint %}

The shared helper is [`NavBaseUsdLib.baseUsd(nav, baseAsset)`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/NavBaseUsdLib.sol): it reads `getPriceDataNoDivergence(baseAsset)`, normalises to 8 decimals, marks the base **stale on genuine base-feed staleness** (a non-positive price or a reverting read; **not** on sequencer-down alone — see above), and **fails to stale** (never reverts the calling feed). `ETH+` (a basket RToken, no base oracle) is unaffected.

***

## Custom price feeds

Custom composite feeds (`PriceType.Custom`, implementing `ICustomPriceFeed`) are deployed for assets that lack a direct USD feed. They are grouped into four families below; each links to its own page with the full composition, constructor, and staleness rules.

{% hint style="warning" %}
**A Custom feed is bound on-chain to the asset it prices.** Registration asks the feed which asset it prices (`underlyingAssetSupported()`) and reverts `CustomFeedAssetMismatch(asset, feed, feedAsset)` if it is not the asset being registered. This covers all three entry points — `registerAsset`, `addPriceFeed` and `addMonitorFeed` — and the read-only `checkPriceOracleSupport`, so tooling can pre-flight the check without a transaction.

Without it, registering feed A for asset B would succeed silently and every B position would be priced at A's price indefinitely: a plausible number, with `stale = false`, `irregular = false`, and no on-chain trace. The exposure is real because feeds are resolved from config by **string key**, across near-identical names — `ezETH` / `ezETHCrossRate`, `osETH` / `osETHCrossRate`, `ETHx` / `ETHxCrossRate`, `rETH` / `rETHCrossRate`, `NXM` / `wNXM`, `weETH_L2` / `weETHCrossRate`.

The check covers the *asset* axis only. A feed's **`baseAsset`** is not bound the same way, so a feed deployed against the wrong base (the osToken feed is built against WETH on mainnet and GNO on Gnosis) would still pass. Verify the base at deploy time.
{% endhint %}

{% hint style="warning" %}
**The rate-feed axis is guarded pre-deploy.** A feed constructed with an external rate oracle — the `Y/X` leg of a [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) feed, for instance — has a third way to be wrong: the right asset, the right base, and the wrong oracle. Confirming the address *is* an aggregator never establishes **which pair** it reports, so each such instance is checked against its declared config before deployment on three axes: **pair identity** from `description()`, a **plausibility band** on the current rate, and **contract-readability**.

The band is not redundant with the pair check. It catches what `description()` cannot — a feed with no description, a renamed one, or one whose description is itself wrong — and rules out cross-class confusion outright, since feeds for different asset classes do not trade in overlapping ranges. The readability check exists because an access-controlled aggregator *reverts* when read, which would otherwise surface as an opaque constructor failure.

**The guard fails closed.** An instance that takes a rate-feed-shaped constructor parameter but declares no guard block **reverts the deploy script** rather than being silently skipped, so the guard cannot quietly stop covering the next feed anyone adds.

Coverage is deliberately **uneven by provider**, and declared rather than assumed. Where a provider's `description()` names the pair, identity is asserted exactly. Where it cannot — RedStone Classic-Push returns the constant string `"Redstone Price Feed"` on *every* feed for *every* pair, so an exact-match assertion is unimplementable for that family rather than merely weak — the config sets `description: "$UNVERIFIABLE"` **with a mandatory written reason**, and the deploy reverts if the reason is missing. The band and readability checks still apply in full; only the pair assertion is dropped, and it is dropped visibly.
{% endhint %}

#### LSTs & LRTs

| Asset                                                                                   | Composition / source                                               | Chains           |
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------- |
| [wstETH](/funds/infrastructure/onchain-accounting/price-feeds/wsteth)                   | `stEthPerToken()` × ETH/USD (stETH/ETH ≡ 1.0)                      | Ethereum         |
| [stETH](/funds/infrastructure/onchain-accounting/price-feeds/steth)                     | `1.0` × ETH/USD (stETH/ETH ≡ 1.0) — market feed demoted to monitor | Ethereum         |
| [wARS](/funds/infrastructure/onchain-accounting/price-feeds/wars)                       | **inverted** Chainlink `USD / ARS`                                 | Ethereum         |
| [weETH](/funds/infrastructure/onchain-accounting/price-feeds/weeth)                     | `getRate()` × ETH/USD (eETH/ETH ≡ 1.0)                             | Ethereum         |
| [eETH](/funds/infrastructure/onchain-accounting/price-feeds/eeth)                       | ETH/USD (eETH/ETH ≡ 1.0)                                           | Ethereum         |
| [rETH](/funds/infrastructure/onchain-accounting/price-feeds/reth)                       | `getExchangeRate()` × ETH/USD                                      | Ethereum         |
| [rsETH](/funds/infrastructure/onchain-accounting/price-feeds/rseth)                     | rate provider × ETH/USD                                            | Ethereum         |
| [ezETH](/funds/infrastructure/onchain-accounting/price-feeds/ezeth)                     | Renzo reporter rate × ETH/USD                                      | Ethereum         |
| [cbETH](/funds/infrastructure/onchain-accounting/price-feeds/cbeth)                     | rate provider × ETH/USD                                            | — *not deployed* |
| [ETHx](/funds/infrastructure/onchain-accounting/price-feeds/ethx)                       | Stader `getExchangeRate()` × ETH/USD                               | Ethereum         |
| [ankrETH](/funds/infrastructure/onchain-accounting/price-feeds/ankreth)                 | ETH/USD ÷ `ratio()`                                                | Ethereum         |
| [osToken (osETH / osGNO)](/funds/infrastructure/onchain-accounting/price-feeds/ostoken) | StakeWise `convertToAssets()` × underlying/USD                     | Ethereum, Gnosis |

{% hint style="info" %}
These LST/LRT feeds are **fundamental** primaries — they read only the protocol's on-chain exchange rate (no embedded market oracle). The market leg lives in a separate [monitor](#primary-feed-and-divergence-monitors): wstETH/weETH watch market `<asset>/USD` feeds, while ezETH, ETHx, osETH, rETH and stETH are watched by a [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) monitor (`ETH/USD × <asset>/ETH`). Where that `<asset>/ETH` leg is an independent **market** feed, a depeg raises `irregular` instead of moving NAV.

{% hint style="info" %}
**ezETH carries two cross-rate monitors, and only one of them is a market feed.** Its original monitor's `ezETH/ETH` leg became *fundamental* (RedStone's multi-feed `ezETH_FUNDAMENTAL`) when RedStone sunset the Classic-Push market feed — leaving a check that compared one fundamental rate against another, which cannot see the market price diverge. A second monitor with a genuine **market** leg was added alongside it rather than replacing it, so the depeg check fires again.

Keeping both is safe because `divergenceBps` is measured against the **worst** single monitor, not the median: the market monitor dominates instead of being averaged away by the fundamental one sitting near 0 bps. Their heartbeats differ deliberately — each is derived from its own feed's measured cadence, not copied from the other.
{% endhint %}

**stETH is priced fundamentally**, at a flat `1.0` against ETH — the same claim wstETH's `stEthPerToken()` and eETH's constant rate encode, and it uses the same unit-rate contract as eETH. It carries **two** monitors: the market `stETH/ETH` cross-rate and a direct Chainlink `stETH/USD` feed. Pricing stETH off a market feed while wstETH was fundamental meant the two disagreed by the full discount during a depeg — 1,000 stETH and the economically identical \~870 wstETH valued 6% apart at a 0.94 print — so legs that should net inside one portfolio (Aave wstETH collateral against a Curve stETH/ETH LP) showed phantom P\&L with no position change. It also made the divergence signal read backwards: wstETH flagged `irregular` during a depeg while stETH, agreeing with itself, flagged nothing.
{% endhint %}

#### Yield-bearing stables

| Asset                                                                       | Composition / source           | Chains   |
| --------------------------------------------------------------------------- | ------------------------------ | -------- |
| [sUSDS](/funds/infrastructure/onchain-accounting/price-feeds/susds)         | `convertToAssets()` × USDS/USD | Ethereum |
| [sUSDe](/funds/infrastructure/onchain-accounting/price-feeds/susde)         | `convertToAssets()` × USDe/USD | Ethereum |
| [sGHO](/funds/infrastructure/onchain-accounting/price-feeds/sgho)           | `convertToAssets()` × GHO/USD  | Ethereum |
| [syrupUSDC](/funds/infrastructure/onchain-accounting/price-feeds/syrupusdc) | Maple rate × USDC/USD          | Ethereum |
| [RWIV](/funds/infrastructure/onchain-accounting/price-feeds/rwiv)           | `convertToAssets()` × USDC/USD | Ethereum |
| [sXDAI](/funds/infrastructure/onchain-accounting/price-feeds/sxdai)         | savings rate × xDAI/USD        | Gnosis   |
| [spUSDC](/funds/infrastructure/onchain-accounting/price-feeds/spusdc)       | `convertToAssets()` × USDC/USD | Ethereum |
| [spUSDT](/funds/infrastructure/onchain-accounting/price-feeds/spusdt)       | `convertToAssets()` × USDT/USD | Ethereum |
| [spETH](/funds/infrastructure/onchain-accounting/price-feeds/speth)         | `convertToAssets()` × WETH/USD | Ethereum |
| [sDAI](/funds/infrastructure/onchain-accounting/price-feeds/sdai)           | savings rate × DAI/USD         | Ethereum |

{% hint style="info" %}
**These float; they are not `$1` assets.** Each prices as a growing redemption rate times its base stablecoin, so its USD price rises above `$1` as the rate accrues — which is what makes it *not* pegged, and why none of them carries a [`pegToleranceBps`](#the-divergence-and-irregular-signal). The `$1` guard belongs to the **base** stablecoin (USDS, USDe, GHO, USDC, USDT, DAI, xDAI) that each of these feeds reads through the NAV. The "stables" grouping here describes the feed shape, not a peg — and `spETH` sits in this table for that reason despite pricing off **WETH**, so it tracks ETH rather than a dollar.
{% endhint %}

{% hint style="warning" %}
**ERC4626 rate legs must be manipulation-resistant.** The `convertToAssets()` feeds (`ERC4626VaultRatePriceFeed`) read the vault's share→asset rate live, so the vault's `totalAssets` accounting must **not** be `balanceOf`-based — a `balanceOf` vault can be inflated by a donation, skewing the rate. The ten shipped vaults use manipulation-resistant accounting; any new ERC4626 rate feed must reject `balanceOf`-based vaults before wiring, and its `baseAsset_` must equal the vault's `asset()` (enforced on-chain — `BaseAssetMismatch`).
{% endhint %}

#### Derived & cross-rate

| Asset                                                                                       | Composition / source                       | Chains                                                      |
| ------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------- |
| [lsETH & OETH (CrossRate)](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) | X/USD × Y/X cross-rate of two CL feeds     | Ethereum                                                    |
| [LDO](/funds/infrastructure/onchain-accounting/price-feeds/ldo)                             | cross-rate composition                     | Ethereum                                                    |
| [ETH+](/funds/infrastructure/onchain-accounting/price-feeds/eth-plus)                       | Reserve RToken basket value                | — *not deployed; ETH+ is priced by a direct Chainlink feed* |
| [NXM / wNXM](/funds/infrastructure/onchain-accounting/price-feeds/nxm)                      | Nexus Mutual RAMM internal price × ETH/USD | Ethereum                                                    |

#### L2 variants

| Asset                                                                               | Composition / source          | Chains                 |
| ----------------------------------------------------------------------------------- | ----------------------------- | ---------------------- |
| [wstETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/wsteth-l2)       | CL wstETH/ETH × ETH/USD       | Arbitrum, Base, Gnosis |
| [weETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/weeth-l2)         | CL weETH/ETH × ETH/USD        | Arbitrum, Base         |
| [rsETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/rseth-l2)         | rate provider × ETH/USD       | Arbitrum               |
| [ezETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/ezeth-l2)         | Renzo reporter rate × ETH/USD | Arbitrum               |
| [cbETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/cbeth-l2)         | CL cbETH/ETH × ETH/USD        | Base                   |
| [syrupUSDC (L2)](/funds/infrastructure/onchain-accounting/price-feeds/syrupusdc-l2) | Maple rate × USDC/USD         | Arbitrum               |

### Governing-leg freshness

A composite has no single honest `(updatedAt, heartbeat)`: it depends on two Chainlink legs — the rate leg with its own heartbeat, plus the base/USD leg with a different one — and the oldest leg and the tightest-heartbeat leg are usually different feeds. Reporting the oldest timestamp against the tightest heartbeat produces a permanent false warning for feeds with a slow leg; pairing it with the loosest heartbeat hides a genuinely stale fast leg.

So `getPriceData(asset)` reports the **governing leg**: the underlying Chainlink leg with the greatest `age / heartbeat` ratio — the input closest to, or furthest past, its own staleness deadline — and surfaces *that leg's* `(updatedAt, chainlinkHeartbeat)`. The pair is a real, matched pair from one actual oracle, and reduces to the obvious answer for a single-leg feed. Each custom feed exposes it as `governingFeedFreshness() → (updatedAt, heartbeat)`:

| Feed shape                                                                                                   | Governing leg                                                                                                                               |
| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Single Chainlink leg (`BaseCustomPriceFeed`, `ERC4626VaultRatePriceFeed`, and the bespoke `ankrETH`)         | The base/USD leg, read from the base asset's own NAV selector via `NavBaseUsdLib.baseFreshness`                                             |
| Two Chainlink legs (`TwoFeedChainlinkPriceFeed`, cross-rate feeds)                                           | Whichever of the rate leg or the base leg has the larger `age / heartbeat` ratio; falls back to the base leg if the rate feed is unreadable |
| [ETH+](/funds/infrastructure/onchain-accounting/price-feeds/eth-plus) (Reserve basket — built, not deployed) | `min(lastSave())` across the active basket, against the Reserve-protocol heartbeat                                                          |
| No trackable Chainlink leg                                                                                   | `(0, 0)` — reported as no timestamp                                                                                                         |

Because the reported heartbeat is the governing leg's, it **overrides the registered config heartbeat** for custom feeds — so a custom-priced asset's reported heartbeat is typically tighter than its registration value.

{% hint style="warning" %}
**The pair is advisory; `stale` is the authoritative verdict.** `(updatedAt, chainlinkHeartbeat)` describe only the governing leg's push freshness. `stale` additionally folds in the rate answer's validity, the L2 sequencer state, and the feed's own configured heartbeat gate — so the pair can read fresh (`age < heartbeat`) while `stale` is `true`. Consumers must gate on `stale` / `sequencerDown` and never recompute staleness from the pair alone.
{% endhint %}

Two further consequences worth knowing: which leg governs **changes between reads** as the ratios cycle, so a two-leg feed's reported heartbeat can alternate between its legs' values; and the feed's own `latestRoundData().updatedAt` is **unchanged** — it still returns the conservative oldest-leg timestamp, which is what feed selection, ranking and the outer heartbeat gate use. The staleness rule itself is unchanged: a custom feed backed by *n* Chainlink legs is stale iff **any** leg is stale.

The helpers are [`NavBaseUsdLib.baseFreshness`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/NavBaseUsdLib.sol) and [`ICustomPriceFeed`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/ICustomPriceFeed.sol).

***

## L2 sequencer uptime check

On Layer 2 chains the NAV Calculator also checks the **Chainlink L2 Sequencer Uptime Feed**; if the sequencer is down or within its grace period, the chain's reading is flagged via `NAV.sequencerDown`. See [Stale prices & sequencer](/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer) for the full staleness and sequencer model.

***

## Inspecting an asset's feeds

Pricing (primary) feeds, monitor feeds, and the per-asset tolerances are each introspectable, and `getPriceDivergence` returns the live divergence read:

```solidity
// Pricing (primary) feeds
function getPriceFeeds(address asset) external view returns (PriceFeedConfig[] memory);
function getPriceFeedCount(address asset) external view returns (uint256);
function getPriceFeedAt(address asset, uint256 index) external view returns (PriceFeedConfig memory);

// Monitor (divergence-only) feeds — reuse the same PriceFeedConfig struct
function getMonitorFeeds(address asset) external view returns (PriceFeedConfig[] memory);
function getMonitorFeedCount(address asset) external view returns (uint256);

// Per-asset tolerances
function getAssetPricing(address asset) external view returns (AssetPricing memory);

// Live divergence read
function getPriceDivergence(address underlyingAsset) external view returns (
    int256 primaryPrice, int256 monitorMedian, uint256 divergenceBps,
    bool irregular, uint256 monitorFeedCount, bool primaryStale, bool primaryAboveMonitors
);

struct PriceFeedConfig {
    address   priceFeed;
    uint8     decimals;
    PriceType priceType;
    uint256   chainlinkHeartbeat;  // staleness window in seconds
}

struct AssetPricing {
    uint32 divergenceToleranceBps;  // stored 0 = divergence check off; unset default only — setDivergenceTolerance rejects 0 (InvalidTolerance), takes (0, 10000]
    uint32 pegToleranceBps;         // stored 0 = $1 peg check off (stablecoins only); setPegTolerance accepts 0, takes [0, 10000]
}
```

Example (illustrative values; `WETH` = `0xC02a…6Cc2` — one Chainlink primary, one Redstone monitor):

```
getPriceFeedCount(0xC02a…6Cc2)     → 1
getPriceFeeds(0xC02a…6Cc2)
→ [ PriceFeedConfig { 0x5f4e…8419, 8, 0 /* Chainlink */, 3600 } ]

getMonitorFeedCount(0xC02a…6Cc2)   → 1
getMonitorFeeds(0xC02a…6Cc2)
→ [ PriceFeedConfig { 0x67F6…6Dc4, 8, 1 /* Redstone */, 86400 } ]

getAssetPricing(0xC02a…6Cc2)       → AssetPricing { 50, 0 }   // 0.5% divergence, no peg check

getPriceDivergence(0xC02a…6Cc2)
→ (392265000000, 392308000000, 11, false, 1, false, false)   // 11 bps apart, within tolerance
```

***

## Admin operations

Feed configuration is privileged (security council Safe, `MANAGER` role). An asset carries one **pricing** feed, plus optional **monitor** feeds and tolerances:

| Action                                        | Call                                                                                                |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| **Register** an asset with its (primary) feed | `registerAsset(asset, priceFeed, priceType, heartbeat)`                                             |
| **Add / remove** a pricing feed               | `addPriceFeed(...)` · `removePriceFeed(asset, priceFeed)` · `removePriceFeedAt(asset, index)`       |
| **Set** the divergence / peg tolerances       | `setDivergenceTolerance(asset, bps)` · `setPegTolerance(asset, bps)`                                |
| **Add / remove** a monitor feed               | `addMonitorFeed(...)` · `removeMonitorFeed(asset, priceFeed)` · `removeMonitorFeedAt(asset, index)` |
| **Remove** the asset and all its feeds        | `unregisterAsset(asset)`                                                                            |

```solidity
function registerAsset(address asset, address priceFeed, PriceType priceType, uint256 chainlinkHeartbeat) external;
function addPriceFeed(address asset, address priceFeed, PriceType priceType, uint256 chainlinkHeartbeat) external;
function removePriceFeed(address asset, address priceFeed) external;     // reverts on the last pricing feed
function removePriceFeedAt(address asset, uint256 index) external;

function setDivergenceTolerance(address asset, uint32 divergenceToleranceBps) external;  // (0, 10000]; 0 reverts InvalidTolerance
function setPegTolerance(address asset, uint32 pegToleranceBps) external;                // [0, 10000]; 0 disables the peg guard
function addMonitorFeed(address asset, address priceFeed, PriceType priceType, uint256 chainlinkHeartbeat) external;
function removeMonitorFeed(address asset, address priceFeed) external;
function removeMonitorFeedAt(address asset, uint256 index) external;
```

`addPriceFeed` requires the new feed's `decimals` to match the asset's existing pricing feeds (`PriceFeedDecimalsMismatch`) and rejects a feed already registered (`PriceFeedAlreadyRegistered`). `addMonitorFeed` requires a divergence tolerance to be set first (`DivergenceToleranceRequired`), the monitor to report **8 decimals** (`MonitorFeedDecimalsMismatch`), and rejects duplicates (`MonitorFeedAlreadyRegistered`). Full per-function detail, roles, and reverts are on the NAV Calculator's [Admin / Manager API](/funds/infrastructure/onchain-accounting/contracts/nav-calculator#admin-manager-api).

{% hint style="warning" %}
An asset must always keep at least one **pricing** feed: `removePriceFeed` reverts with `CannotRemoveLastFeed`. Monitor feeds are optional — the last one **can** be removed. Fully removing pricing for an asset is done via `unregisterAsset`, which should only happen after the asset has been withdrawn from every portfolio.
{% endhint %}


# stETH

Prices stETH fundamentally at 1.0 × ETH/USD, with the Chainlink stETH/ETH market feed retained as a divergence monitor rather than the primary.

stETH is priced **fundamentally**: `stETH/USD = 1.0 × ETH/USD`, holding stETH/ETH at parity on Lido's redemption invariant. The Chainlink stETH/ETH **market** feed is still read, but as a **divergence monitor** rather than the price source.

* **Chain:** Ethereum
* **Primary** — `stETHFundamental`: `1.0 × ETH/USD`, a unit-rate feed. Deployed from [`eETH_CustomPriceFeed.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/protocols/eETH_CustomPriceFeed.sol), so the contract name shown on explorers reads **eETH** — expected, not a mis-deployment.
* **Monitor** — `stETHCrossRate`: `ETH/USD × stETH/ETH` with the Chainlink stETH/ETH market feed. Source [`stETH_CustomPriceFeed.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/protocols/stETH_CustomPriceFeed.sol).

***

## Why fundamental, with the market feed kept as a monitor

{% hint style="info" %}
The market CrossRate **was** the primary until a price-feed audit reversed it.

Pricing stETH off a market feed makes the NAV move with secondary-market noise on an asset whose redemption value is defined by Lido at parity. That is the same reasoning applied to [wstETH](/funds/infrastructure/onchain-accounting/price-feeds/wsteth) (`stEthPerToken()` × ETH/USD) and eETH (`1.0` × ETH/USD) — so the fundamental primary makes stETH **consistent with its siblings** rather than an exception.

Demoting the market feed to a monitor keeps its signal without letting it set the price: a divergence between the fundamental price and the market feed beyond tolerance raises the position's `irregular` flag, surfacing a depeg rather than silently repricing the book.
{% endhint %}

***

## Base asset

The `ETH/USD` leg is read **from the NAV** rather than from a second Chainlink feed held by the contract, so stETH cannot be valued against an ETH price the rest of the NAV disagrees with — it inherits the NAV's own staleness and divergence checks.


# wstETH

wstETH is the non-rebasing wrapped form of Lido's staked ETH (stETH).

wstETH is the non-rebasing wrapped form of Lido's staked ETH (stETH). It is priced **purely from its on-chain redemption rate** — no market oracle sits in the pricing path. This `ICustomPriceFeed` adapter reads Lido's wrap rate and scales it by the NAV's own ETH/USD. Any market divergence (e.g. a stETH depeg) is surfaced by a market **monitor** feed, not by moving this price.

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [wstETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/wsteth-l2).
{% endhint %}

***

## Approach

`stEthPerToken()` × ETH/USD — the wrap rate gives ETH per wstETH (treating **stETH/ETH ≡ 1.0**, Lido's fundamental redemption invariant), then ETH/USD converts to USD. This is a pure-fundamental [`BaseCustomPriceFeed`](/funds/infrastructure/onchain-accounting/price-feeds#supported-feed-types) subclass: it implements only the `_rate()` hook and carries no market stETH/ETH leg. The ETH/USD term is not a hardcoded oracle — it is the NAV's own price for WETH (via `getPriceDataNoDivergence(WETH)`).

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-8d875968ef7af0a4cec66c602e09ebf3aba9ab5a%2Fprice-wsteth.svg?alt=media" alt="Left-to-right flowchart of the wstETH (Mainnet) price feed: the on-chain wrap rate scaled by the NAV&#x27;s ETH/USD, with a staleness gate."><figcaption><p>wstETH (Mainnet) price feed — the on-chain wrap rate scaled by the NAV's ETH/USD, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own primary feed — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = stEthPerToken (18) × (ETH/USD, NAV-selected) (8) / 1e18      // USD, 8 dec
```

| Input           | Source                                             | Description                        |
| --------------- | -------------------------------------------------- | ---------------------------------- |
| `stEthPerToken` | `IwstETH.stEthPerToken()` on wstETH (18 dec)       | stETH per 1 wstETH (the wrap rate) |
| ETH/USD         | **NAV** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price   |

The wrap rate `stEthPerToken` increases monotonically as Lido staking rewards accrue. Pricing off `stEthPerToken` alone assumes 1 stETH is redeemable \~1:1 for ETH; a real stETH depeg does **not** move this price but instead trips the divergence monitor (below). Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address wstEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `wstEth_`    | wstETH token address (also the supported underlying)                                       |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `stEthPerToken()` returns `0`, or if the NAV reports the base asset (WETH) primary feed stale. There is no market-leg heartbeat — the wrap-rate call has no independent heartbeat, and the only external timestamp is the NAV's selected WETH feed, which supplies the reported `updatedAt`.

***

## Divergence monitors

wstETH carries a **divergence tolerance of 250 bps** and is watched by two market **monitor** feeds — a Redstone and a Chainlink wstETH/USD feed. On each NAV read the primary is compared against them; if the worst-case deviation exceeds 250 bps the asset is flagged `irregular` (a signal, not a price change). See [Primary feed and divergence monitors](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors).

***

## Chains

Deployed on **Ethereum**. For the rollup deployments using a direct Chainlink wstETH/ETH feed, see [wstETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/wsteth-l2).


# wARS

Prices wARS by INVERTING a Chainlink USD/ARS feed — the only feed direction available for the asset.

Prices **wARS** ("Peso Argentino") by **inverting** a Chainlink `USD / ARS` feed:

```
price = 1e8 × 10^feedDecimals / answer
```

* **Chain:** Ethereum
* **Rate feed:** Chainlink `USD / ARS` — `description() == "USD / ARS"`, 8 decimals
* **Source:** [`InvertedChainlinkCustomPriceFeed.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/protocols/InvertedChainlinkCustomPriceFeed.sol)

This is a **generic inverter**, not a wARS-specific contract: use it for any asset whose only Chainlink coverage is quoted the wrong way round.

***

## Why an inverting contract is necessary

{% hint style="danger" %}
A `USD / X` feed is the **reciprocal** of what the NAV expects. Every primary feed registered here must answer *"USD per one asset"*; this feed answers *"how many ARS per one USD"* — roughly `1583` at the time of writing.

Registering it directly as a `type: "Chainlink"` primary would value 1 wARS at about **$1,583 instead of about $0.00063** — an over-report of roughly **2.5 million×** (1583²).

**Nothing in the config schema encodes feed direction, and no preflight guard checks it.** That is why the inversion has to live in a contract rather than in configuration: there is no place for "this feed is backwards" to be expressed as data, and no gate that would catch it if it were wrong.
{% endhint %}

**There is more than one ARS feed on mainnet.** An earlier revision of this contract's own documentation asserted that the inverted feed was the only one; that was false and was caught in review. A direct `ARS / USD` feed also exists. The inverted feed is the one KPK uses, and the choice is deliberate rather than forced — recorded here because the stronger claim is the kind that gets repeated.

***

## Safety properties

* **Division last.** The inversion computes `1e8 × 10^feedDecimals` first and divides by the answer at the end, so precision is not lost to an early truncation.
* **Runtime band.** `MIN_ANSWER` / `MAX_ANSWER` bound the raw feed answer. An answer outside the band is rejected rather than inverted — without it, an answer near zero would invert to an unbounded price.
* **Feed shape re-checked per read.** The feed's unit and decimals are cached at construction and re-asserted on every read, so a feed that changes shape underneath the contract fails closed.
* **`BASE_ASSET` is `address(0)`.** The inversion produces USD directly and composes with nothing, so there is no base-asset leg to get wrong.


# weETH

weETH is Ether.fi's non-rebasing wrapped eETH.

weETH is Ether.fi's non-rebasing wrapped eETH. It is priced **purely from its on-chain exchange rate** — no market oracle sits in the pricing path. This `ICustomPriceFeed` adapter reads the weETH contract's `getRate()` and scales it by the NAV's own ETH/USD. Any market divergence is surfaced by a market **monitor** feed, not by moving this price.

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [weETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/weeth-l2).
{% endhint %}

***

## Approach

`getRate()` × ETH/USD — `getRate()` gives eETH per weETH, which is ETH per weETH when treating **eETH/ETH ≡ 1.0** (eETH is a \~1:1 rebasing claim on staked ETH); ETH/USD then converts to USD. This is a pure-fundamental [`BaseCustomPriceFeed`](/funds/infrastructure/onchain-accounting/price-feeds#supported-feed-types) subclass: it implements only the `_rate()` hook and carries no market weETH/ETH Chainlink leg. The ETH/USD term is the NAV's own price for WETH (via `getPriceDataNoDivergence(WETH)`), not a hardcoded oracle.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-60a7de93548c649abd7ecb12ea0660daad006abe%2Fprice-weeth.svg?alt=media" alt="Left-to-right flowchart of the weETH (Mainnet) price feed: the on-chain exchange rate scaled by the NAV&#x27;s ETH/USD, with a staleness gate."><figcaption><p>weETH (Mainnet) price feed — the on-chain exchange rate scaled by the NAV's ETH/USD, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own primary feed — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = getRate() (18) × (ETH/USD, NAV-selected) (8) / 1e18      // USD, 8 dec
```

| Input     | Source                                             | Description                      |
| --------- | -------------------------------------------------- | -------------------------------- |
| `getRate` | `IweETH.getRate()` on weETH (18 dec)               | eETH per 1 weETH                 |
| ETH/USD   | **NAV** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price |

`getRate()` is the weETH contract's authoritative eETH-per-weETH conversion; since 1 eETH ≈ 1 ETH, it doubles as ETH per weETH. A real depeg does **not** move this price but instead trips the divergence monitor (below). Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address weEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `weEth_`     | weETH token address (also the supported underlying; used for `getRate()`)                  |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `getRate()` returns `0`, or if the NAV reports the base asset (WETH) primary feed stale. There is no market-leg heartbeat — the rate call has no independent heartbeat, and the only external timestamp is the NAV's selected WETH feed, which supplies the reported `updatedAt`.

***

## Divergence monitors

weETH carries a **divergence tolerance of 250 bps** and is watched by a Redstone weETH/USD market **monitor** feed. If the primary deviates from the monitor beyond 250 bps the asset is flagged `irregular` (a signal, not a price change). See [Primary feed and divergence monitors](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors).

***

## Chains

Deployed on **Ethereum**. The L2 variant instead reads a Chainlink weETH/eETH exchange-rate feed, since weETH is bridged there and has no on-chain rate — see [weETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/weeth-l2).


# eETH

eETH is Ether.fi's rebasing staked-ETH token (weETH is its non-rebasing wrapper).

eETH is Ether.fi's rebasing staked-ETH token (weETH is its non-rebasing wrapper). Because eETH is a \~1:1 rebasing claim on staked ETH, its fundamental (redeemable) value tracks ETH one-for-one, so it is priced **directly off the NAV's ETH/USD** — `eETH/ETH ≡ 1.0`. Any market divergence is surfaced by a market **monitor** feed, not by moving this price.

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

***

## Approach

`1.0 × ETH/USD` — this is a pure-fundamental [`BaseCustomPriceFeed`](/funds/infrastructure/onchain-accounting/price-feeds#supported-feed-types) subclass whose `_rate()` hook returns the constant `1e18` (eETH/ETH ≡ 1.0), so the price is exactly the NAV's ETH/USD. The old feed back-derived eETH/ETH from the weETH/ETH Chainlink feed and `weETH.getRate()`; that market leg is gone. The ETH/USD term is the NAV's own price for WETH (via `getPriceDataNoDivergence(WETH)`), not a hardcoded oracle.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-1730684cf07b74214f596027b73622497944083c%2Fprice-eeth.svg?alt=media" alt="Left-to-right flowchart of the eETH price feed: a constant unit rate scaled by the NAV&#x27;s ETH/USD, with a staleness gate."><figcaption><p>eETH price feed — a constant unit rate (eETH/ETH ≡ 1.0) scaled by the NAV's ETH/USD, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own primary feed — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = 1e18 (18) × (ETH/USD, NAV-selected) (8) / 1e18      // USD, 8 dec — i.e. price = ETH/USD
```

| Input   | Source                                             | Description                        |
| ------- | -------------------------------------------------- | ---------------------------------- |
| rate    | constant `1e18` (eETH/ETH ≡ 1.0)                   | 1 eETH is redeemable \~1:1 for ETH |
| ETH/USD | **NAV** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price   |

Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address eEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `eEth_`      | eETH token address (the supported underlying)                                              |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | the registered base asset whose USD price is the eETH price (WETH)                         |

***

## Staleness

`getLatestPrice()` returns stale only if the NAV reports the base asset (WETH) primary feed stale — the rate is a constant, so there is no rate call or heartbeat to gate. The reported `updatedAt` comes from the NAV's selected WETH feed.

***

## Divergence monitors

A market **monitor** feed can be attached to flag a real eETH depeg via NAVCalculator's divergence check; on Ethereum mainnet eETH currently ships with no monitor (no divergence tolerance set). See [Primary feed and divergence monitors](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors).

***

## Chains

Deployed on **Ethereum**.


# rETH

rETH is Rocket Pool's non-rebasing staked-ETH token.

rETH is Rocket Pool's non-rebasing staked-ETH token. No direct rETH/USD Chainlink feed is used; this `ICustomPriceFeed` adapter combines the protocol's own exchange rate with the NAV's primary ETH/USD price. An independent [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) **monitor** feed (`ETH/USD × rETH/ETH`, market `rETH/ETH` leg) is registered as a [divergence monitor](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors) — a market depeg flags rETH `irregular` without moving its price.

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

***

## Approach

`getExchangeRate()` × ETH/USD.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-f1970d8a126f61668ff014dceec63057243557dc%2Fprice-reth.svg?alt=media" alt="Left-to-right flowchart of the rETH price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>rETH price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = getExchangeRate() (18) × (ETH/USD) (8) / 1e18      // USD, 8 dec
```

| Input             | Source                                                  | Description                      |
| ----------------- | ------------------------------------------------------- | -------------------------------- |
| `getExchangeRate` | `IrETH.getExchangeRate()` on rETH (18 dec)              | ETH backing 1 rETH               |
| ETH/USD           | **NAV base** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price |

`getExchangeRate()` is Rocket Pool's canonical rETH/ETH ratio, updated on-chain by the protocol's oracle network and increasing as staking rewards accrue. Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address rEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `rEth_`      | rETH token address (also the supported underlying)                                         |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `getExchangeRate()` returns `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The `getExchangeRate()` rate call has no independent heartbeat.

***

## Chains

Deployed on **Ethereum**.


# rsETH

rsETH is Kelp DAO's liquid restaked ETH token.

rsETH is Kelp DAO's liquid restaked ETH token. No direct rsETH/USD Chainlink feed exists, so this `ICustomPriceFeed` adapter combines Kelp's rate provider with the NAV's primary ETH/USD price.

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [rsETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/rseth-l2).
{% endhint %}

***

## Approach

`getLatestRate()` (rate provider) × ETH/USD.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-f37d6504af6c0d88740f709978c45ce1e9e74138%2Fprice-rseth.svg?alt=media" alt="Left-to-right flowchart of the rsETH (Mainnet) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>rsETH (Mainnet) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = getLatestRate() (18) × (ETH/USD) (8) / 1e18      // USD, 8 dec
```

| Input           | Source                                                  | Description                      |
| --------------- | ------------------------------------------------------- | -------------------------------- |
| `getLatestRate` | `IRsETHRateProvider.getLatestRate()` (18 dec)           | rsETH/ETH exchange rate          |
| ETH/USD         | **NAV base** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price |

The rate comes from Kelp DAO's Multi-Chain Rate Provider, updated by Kelp's own oracle infrastructure. The rate provider address is validated only as a non-zero address (not as a Chainlink-shaped oracle). Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address rsEth_,
    address rateProvider_,
    address nav_,
    address baseAsset_
)
```

| Parameter       | Description                                                                                |
| --------------- | ------------------------------------------------------------------------------------------ |
| `rsEth_`        | rsETH token address (also the supported underlying)                                        |
| `rateProvider_` | Kelp DAO Multi-Chain Rate Provider                                                         |
| `nav_`          | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`    | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `getLatestRate()` returns `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The rate provider call has no independent heartbeat.

***

## Chains

Deployed on **Ethereum**. For the rollup deployment, see [rsETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/rseth-l2).


# ezETH

ezETH is Renzo's liquid restaked ETH token.

ezETH is Renzo's liquid restaked ETH token. No direct ezETH/USD Chainlink feed exists on mainnet, so this `ICustomPriceFeed` adapter derives the backing ratio from Renzo's protocol TVL and the ezETH supply, then converts to USD using the NAV's primary ETH/USD price. An independent [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) **monitor** feed (`ETH/USD × ezETH/ETH`) is registered as a [divergence monitor](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors) — if its ezETH rate diverges from Renzo's on-chain rate beyond tolerance, ezETH is flagged `irregular` without moving its price.

{% hint style="info" %}
**ezETH has two monitors, and only one of them is a market feed.** One cross-rate monitor's rate leg is RedStone's multi-feed **`ezETH_FUNDAMENTAL`**. Against a fundamental primary that puts *both* sides of the comparison on fundamentals, so it catches a reporting fault between two independent computations of the same claim but **not** a market depeg — the thing the tolerance exists to detect.

The second cross-rate monitor carries a genuine **market** leg, which is what makes the tolerance meaningful. Because `divergenceBps` is taken against the **worst** single monitor rather than the median, the market monitor dominates instead of being diluted by the fundamental one sitting near 0 bps, so ezETH's 250 bps tolerance can fire.

The market feed is itself deprecated by RedStone, which is deliberate and bounded: a monitor can never price NAV, and monitors are heartbeat-gated, so a frozen feed **self-excludes** from the divergence check rather than contributing a stale rate — `monitorFeedCount` simply drops from 2 to 1, making the degradation observable. Its heartbeat is derived from that feed's own measured cadence rather than copied from the other monitor's. Should *both* monitors go unreadable, ezETH is reported in [`NAV.monitorsUnhealthyPriceAssets`](/funds/infrastructure/onchain-accounting/concepts/stale-prices-and-sequencer#when-no-monitor-answered-monitorsunhealthypriceassets), so the loss of the check is visible rather than presenting as a clean `irregular` flag.

Its pair identity is **not** assertable from the feed contract: RedStone Classic-Push feeds return the constant `"Redstone Price Feed"` from `description()`, carrying no pair information, so the [rate-feed guard](/funds/infrastructure/onchain-accounting/price-feeds#custom-price-feeds) declares this leg `$UNVERIFIABLE` with a written reason and holds it to the plausibility band and readability checks instead.
{% endhint %}

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [ezETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/ezeth-l2).
{% endhint %}

***

## Approach

`calculateTVLs().totalTVL ÷ totalSupply` (ezETH/ETH) × ETH/USD.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-5515f2364a84e13fd14e4955453fb5b63e7e6f3f%2Fprice-ezeth.svg?alt=media" alt="Left-to-right flowchart of the ezETH (Mainnet) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>ezETH (Mainnet) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
ezEthRate = totalTVL (18) × 1e18 / ezETH.totalSupply()      // ezETH/ETH, 18 dec
price     = ezEthRate (18) × (ETH/USD) (8) / 1e18           // USD, 8 dec
```

| Input         | Source                                                  | Description                                                       |
| ------------- | ------------------------------------------------------- | ----------------------------------------------------------------- |
| `totalTVL`    | `IRestakeManager.calculateTVLs()` (18 dec)              | Total ETH value restaked via Renzo (read defensively — see below) |
| `totalSupply` | `IERC20(ezETH).totalSupply()`                           | Circulating ezETH supply                                          |
| ETH/USD       | **NAV base** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price                                  |

The rate is taken directly from Renzo protocol state rather than a market price. If `calculateTVLs()` cannot be read or `totalSupply()` is `0`, the feed is marked stale. Output decimals are inherited from the ETH/USD feed (8).

Only the **third** return value of `calculateTVLs()` — `totalTVL`, a plain `uint256` — is read; the two nested arrays it also returns are never decoded. That matters because `try`/`catch` does not intercept a return-data **decode** failure (the decode runs in the calling frame, after the call returns), and a nested `uint256[][]` is the most failure-prone shape to decode: a bad head offset or an absurd length would propagate past the `catch` and revert the feed instead of degrading to stale.

***

## Constructor

```solidity
constructor(
    address ezEth_,
    address restakeManager_,
    address nav_,
    address baseAsset_
)
```

| Parameter         | Description                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------ |
| `ezEth_`          | ezETH token address (also the supported underlying)                                        |
| `restakeManager_` | Renzo RestakeManager (exposes `calculateTVLs()`)                                           |
| `nav_`            | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`      | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `calculateTVLs()` reverts, if `totalSupply()` is `0`, or if the derived rate is `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed.

{% hint style="warning" %}
`calculateTVLs()` reads Renzo's internal oracles, whose freshness is **not** independently checked here — only the NAV's base-asset staleness is. If Renzo's oracles are stale the TVL figure may be stale undetected. This is an architectural constraint of Renzo on mainnet; the L2 adapter avoids it by reading `xRenzoDeposit.getRate()` directly.
{% endhint %}

***

## Chains

Deployed on **Ethereum**. For the rollup deployment, see [ezETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/ezeth-l2).


# cbETH

cbETH is Coinbase's wrapped staked-ETH token.

cbETH is Coinbase's wrapped staked-ETH token. No direct cbETH/USD Chainlink feed is used; this `ICustomPriceFeed` adapter combines the token's own exchange rate with an ETH/USD price read from the NAV's own primary feed.

{% hint style="warning" %}
**This feed is built and tested but deployed on no chain.** Mainnet cbETH is not a held asset, so it is not registered on any NAV Calculator. cbETH **is** priced on Base, by the separate [cbETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/cbeth-l2) feed — a different composition, using a Chainlink cbETH/ETH market rate.

The contract is kept deliberately rather than deleted: it reads cbETH's own `exchangeRate()`, which is **fundamental** and therefore independent of the market feed the L2 variant composes — exactly the shape a divergence monitor needs, should mainnet cbETH ever be held.
{% endhint %}

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [cbETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/cbeth-l2).
{% endhint %}

***

## Approach

`exchangeRate()` × ETH/USD.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-c33550b3e4980fb7b0d013794b57dd6901d37be5%2Fprice-cbeth.svg?alt=media" alt="Left-to-right flowchart of the cbETH (Mainnet) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>cbETH (Mainnet) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = exchangeRate() (18) × (ETH/USD) (8) / 1e18      // USD, 8 dec
```

| Input          | Source                                              | Description                      |
| -------------- | --------------------------------------------------- | -------------------------------- |
| `exchangeRate` | `IcbETH.exchangeRate()` on cbETH (18 dec)           | ETH per 1 cbETH                  |
| ETH/USD        | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price |

`exchangeRate()` is read directly from the cbETH contract and grows as staking rewards accrue. Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address cbEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `cbEth_`     | cbETH token address (also the supported underlying)                                        |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | The registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `exchangeRate()` returns `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The `exchangeRate()` call has no independent heartbeat.

***

## Chains

Deployed on **Ethereum**. For the Base deployment using a direct Chainlink cbETH/ETH feed, see [cbETH (L2)](/funds/infrastructure/onchain-accounting/price-feeds/cbeth-l2).


# ETHx

ETHx is Stader's liquid staked-ETH token.

ETHx is Stader's liquid staked-ETH token. No direct ETHx/USD Chainlink feed is used; this `ICustomPriceFeed` adapter combines Stader's exchange rate with an ETH/USD price read from the NAV's own primary feed. An independent [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) **monitor** feed (`ETH/USD × ETHx/ETH`, with a **Chainlink** `ETHx/ETH` leg) is registered as a [divergence monitor](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors) — a market depeg flags ETHx `irregular` without moving its price. That cross-rate is ETHx's **only** monitor: the direct Redstone `ETHx/USD` monitor was removed with no replacement when Redstone retired its ETHx push feeds, and Chainlink publishes no `ETHx/USD`. The cross-rate survived because its rate leg could be migrated from Redstone to Chainlink's `ETHx/ETH`, so ETHx keeps its 250 bps divergence signal.

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

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

***

## Approach

`getExchangeRate()` (StakePoolsManager) × ETH/USD.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-855dd3c43a7f72caa988f1d783865462992e504b%2Fprice-ethx.svg?alt=media" alt="Left-to-right flowchart of the ETHx price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>ETHx price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = getExchangeRate() (18) × (ETH/USD) (8) / 1e18      // USD, 8 dec
```

| Input             | Source                                                | Description                      |
| ----------------- | ----------------------------------------------------- | -------------------------------- |
| `getExchangeRate` | `IStaderStakePoolsManager.getExchangeRate()` (18 dec) | ETH per 1 ETHx                   |
| ETH/USD           | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec)   | the NAV's primary WETH/USD price |

The rate is read from Stader's StakePoolsManager and grows as staking rewards accrue. The constructor additionally requires the manager address to be a deployed contract (non-zero `code.length`). Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address ethx_,
    address stakePoolsManager_,
    address nav_,
    address baseAsset_
)
```

| Parameter            | Description                                                                                |
| -------------------- | ------------------------------------------------------------------------------------------ |
| `ethx_`              | ETHx token address (also the supported underlying)                                         |
| `stakePoolsManager_` | Stader StakePoolsManager (exposes `getExchangeRate()`)                                     |
| `nav_`               | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`         | The registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `getExchangeRate()` returns `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The rate call has no independent heartbeat.

***

## Chains

Deployed on **Ethereum**.


# ankrETH

ankrETH is Ankr's reward-bearing staked-ETH token whose ETH value grows over time.

ankrETH is Ankr's reward-bearing staked-ETH token whose ETH value grows over time. No direct ankrETH/USD Chainlink feed is used; this `ICustomPriceFeed` adapter inverts the token's `ratio()` and applies an ETH/USD price read from the NAV's own primary feed.

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

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

***

## Approach

ETH/USD ÷ `ratio()` — `ratio()` is ankrETH-per-ETH, so its inverse is ETH per ankrETH.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-0c29caf92387e86dc2b49877c68b4f6823dbd588%2Fprice-ankreth.svg?alt=media" alt="Left-to-right flowchart of the ankrETH price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>ankrETH price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = (ETH/USD) (8) × 1e18 / ratio() (18)      // USD, 8 dec
```

| Input   | Source                                              | Description                                     |
| ------- | --------------------------------------------------- | ----------------------------------------------- |
| `ratio` | `IankrETH.ratio()` on ankrETH (18 dec)              | ankrETH per 1 ETH (decreases as rewards accrue) |
| ETH/USD | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price, the numerator |

Because `ratio()` is ankrETH-per-ETH, ETH-per-ankrETH = `1e18 / ratio`; the `1e18` numerator cancels the ratio's 18-decimal scale, leaving the ETH/USD feed's 8 decimals. As staking rewards accrue, `ratio()` decreases, so the derived ankrETH price rises. Output decimals are inherited from the ETH/USD feed (8).

***

## Constructor

```solidity
constructor(
    address ankrEth_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `ankrEth_`   | ankrETH token address (also the supported underlying)                                      |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | The registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns stale if `ratio()` returns `0`, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The `ratio()` call has no independent heartbeat.

***

## Chains

Deployed on **Ethereum**.


# osToken (osETH / osGNO)

Custom price feed for StakeWise V3 osTokens.

Custom price feed for **StakeWise V3 osTokens**. No direct osToken/USD feed exists, so the price is composed from the StakeWise vault controller's osToken→underlying conversion rate and the NAV's own underlying/USD base price. A single generic adapter prices **osETH** on Ethereum (underlying ETH) and **osGNO** on Gnosis (underlying GNO) via separate instances. On mainnet, **osETH** also carries an independent [cross-rate](/funds/infrastructure/onchain-accounting/price-feeds/cross-rate) **monitor** feed (`ETH/USD × osETH/ETH`, with a Redstone `osETH/ETH` leg) registered as a [divergence monitor](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors) — a market depeg flags osETH `irregular` without moving its price.

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

***

## Approach

`OsTokenVaultController.convertToAssets(1e18)` × NAV base underlying/USD (`getPriceDataNoDivergence(WETH)` on mainnet, `getPriceDataNoDivergence(GNO)` on Gnosis)

{% hint style="info" %}
The base/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-0f61d7c336f97037d1738f24e3a898d96fb4708a%2Fprice-ostoken.svg?alt=media" alt="Left-to-right flowchart of the osToken (osETH / osGNO) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>osToken (osETH / osGNO) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = convertToAssets(1e18) × (underlying/USD) / 1e18
```

| Input                   | Source                                                                                        | Description                                                                   |
| ----------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `convertToAssets(1e18)` | `IOsTokenVaultController.convertToAssets`                                                     | Underlying backing per 1 osToken (18 dec)                                     |
| `underlying/USD`        | NAV base — `getPriceDataNoDivergence(WETH)` on mainnet, `getPriceData(GNO)` on Gnosis (8 dec) | the NAV's primary underlying/USD price — ETH/USD for osETH, GNO/USD for osGNO |

`convertToAssets` returns the underlying-per-osToken exchange rate, which rises as StakeWise staking rewards accrue. The underlying's USD price is read from the NAV's own primary feed — ETH/USD via `getPriceDataNoDivergence(WETH)` on mainnet, GNO/USD via `getPriceDataNoDivergence(GNO)` on Gnosis. The divisor is the fixed `1e18`. Output is **8 decimals** (inherited from the NAV's base price).

***

## Constructor

```solidity
constructor(
    address osToken_,
    address osTokenVaultController_,
    address nav_,
    address baseAsset_
)
```

| Parameter                 | Description                                                                                |
| ------------------------- | ------------------------------------------------------------------------------------------ |
| `osToken_`                | osToken address (osETH or osGNO; the supported underlying asset)                           |
| `osTokenVaultController_` | StakeWise `OsTokenVaultController` for this osToken (must be a contract)                   |
| `nav_`                    | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`              | The registered base asset whose USD price is read (WETH on mainnet, GNO on Gnosis)         |

***

## Staleness

The underlying/USD leg has no independent heartbeat here — its freshness comes from the NAV: the result is marked stale if the NAV reports the base asset (WETH on mainnet, GNO on Gnosis) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected feed. The result is also marked stale if the underlying/USD answer is `≤ 0` or if `convertToAssets(1e18)` returns `0`. The `convertToAssets` call has no independent age check — it is a live view on the StakeWise controller.

***

## Chains

Deployed on **Ethereum** (osETH, underlying ETH/USD) and **Gnosis** (osGNO, underlying GNO/USD), one instance per osToken.


# sUSDS

Custom price feed for Sky/MakerDAO Savings USDS.

Custom price feed for **Sky/MakerDAO Savings USDS**. sUSDS is an ERC-4626 vault share that accrues value as the Sky Savings Rate (SSR) accumulates. The price is derived from the vault's current share-to-asset conversion rate multiplied by the Chainlink USDS/USD feed.

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

***

## Approach

`sUSDS.convertToAssets(1 sUSDS)` × USDS/USD (from the NAV's primary feed)

{% hint style="info" %}
The USDS/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-4764db09616e3cc64f422d9015ca569e45e912f7%2Fprice-susds.svg?alt=media" alt="Left-to-right flowchart of the sUSDS price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>sUSDS price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = sUSDS.decimals()                       // vault share decimals
rate     = sUSDS.convertToAssets(10^decimals)     // USDS per 1 sUSDS (vault decimals)
price    = rate × USDS_USD / 10^decimals
```

| Input               | Source                                              | Description                                                    |
| ------------------- | --------------------------------------------------- | -------------------------------------------------------------- |
| `convertToAssets()` | `IERC4626(sUSDS)`                                   | USDS backing per 1 sUSDS share at the current SSR-accrued rate |
| `USDS_USD`          | NAV base — `getPriceDataNoDivergence(USDS)` (8 dec) | the NAV's primary USDS/USD price                               |

The ERC-4626 `convertToAssets` call returns the current exchange rate including all accrued Sky Savings Rate interest. The vault decimals are read once at deploy. Result carries the feed's **8 decimals**. The USDS/USD term is read from the NAV's primary feed.

***

## Constructor

```solidity
constructor(
    address sUsds_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `sUsds_`     | The sUSDS vault address (also used to read vault decimals)                                                                                  |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                             |
| `baseAsset_` | The registered base stablecoin whose USD price is read (USDS); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the NAV reports the base asset (USDS) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected USDS feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Ethereum mainnet** only.


# sUSDe

Custom price feed for Ethena Staked USDe.

Custom price feed for **Ethena Staked USDe**. sUSDe is an ERC-4626 vault share that accrues yield from Ethena's delta-neutral strategy. The price is derived from the vault's current share-to-USDe conversion rate multiplied by the Chainlink USDe/USD feed.

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

***

## Approach

`sUSDe.convertToAssets(1 sUSDe)` × USDe/USD (from the NAV's primary feed)

{% hint style="info" %}
The USDe/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-d3183803f380537b39e264464d9bdada49007151%2Fprice-susde.svg?alt=media" alt="Left-to-right flowchart of the sUSDe price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>sUSDe price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = sUSDe.decimals()                       // vault share decimals
rate     = sUSDe.convertToAssets(10^decimals)     // USDe per 1 sUSDe (vault decimals)
price    = rate × USDe_USD / 10^decimals
```

| Input               | Source                                              | Description                                                    |
| ------------------- | --------------------------------------------------- | -------------------------------------------------------------- |
| `convertToAssets()` | `IERC4626(sUSDe)`                                   | USDe backing per 1 sUSDe at the current accumulated yield rate |
| `USDe_USD`          | NAV base — `getPriceDataNoDivergence(USDe)` (8 dec) | the NAV's primary USDe/USD price                               |

`convertToAssets` is linear in shares, so dividing by the same `10^decimals` used as input yields the exchange rate (totalAssets/totalSupply). Vault decimals are read once at deploy. Result carries the feed's **8 decimals**.

***

## Constructor

```solidity
constructor(
    address sUsde_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `sUsde_`     | The sUSDe vault address (also used to read vault decimals)                                                                                  |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                             |
| `baseAsset_` | The registered base stablecoin whose USD price is read (USDe); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the NAV reports the base asset (USDe) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected USDe feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Ethereum mainnet** only.


# sGHO

Custom price feed for Aave Savings GHO (sGHO).

Custom price feed for **Aave Savings GHO (sGHO)**. sGHO is an ERC-4626 vault share that accrues value as GHO savings yield accumulates. The price is derived from the vault's current share-to-asset conversion rate multiplied by the Chainlink GHO/USD feed.

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

***

## Approach

`sGHO.convertToAssets(1 sGHO)` × GHO/USD (from the NAV's primary feed)

{% hint style="info" %}
The GHO/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-da71259e3f093361500a1a710a8dcca1e4b630ba%2Fprice-sgho.svg?alt=media" alt="Left-to-right flowchart of the sGHO price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>sGHO price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = sGHO.decimals()                        // vault share decimals
rate     = sGHO.convertToAssets(10^decimals)       // GHO per 1 sGHO (vault decimals)
price    = rate × GHO_USD / 10^decimals
```

| Input               | Source                                             | Description                                                      |
| ------------------- | -------------------------------------------------- | ---------------------------------------------------------------- |
| `convertToAssets()` | `IERC4626(sGHO)`                                   | GHO backing per 1 sGHO share at the current savings-accrued rate |
| `GHO_USD`           | NAV base — `getPriceDataNoDivergence(GHO)` (8 dec) | the NAV's primary GHO/USD price                                  |

The ERC-4626 `convertToAssets` call returns the current exchange rate including all accrued savings yield. The vault decimals are read once at deploy. Result carries the feed's **8 decimals**. The GHO/USD term is read from the NAV's primary feed.

***

## Constructor

```solidity
constructor(
    address sGho_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `sGho_`      | The sGHO vault address (also used to read vault decimals)                                                                                  |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                            |
| `baseAsset_` | The registered base stablecoin whose USD price is read (GHO); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the NAV reports the base asset (GHO) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected GHO feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Ethereum mainnet** only.


# syrupUSDC

Custom price feed for Maple Finance syrupUSDC on Ethereum mainnet.

Custom price feed for **Maple Finance syrupUSDC** on Ethereum mainnet. syrupUSDC is an ERC-4626 vault share representing a deposit in Maple's USDC lending pool. The price is derived from the vault's current share-to-USDC conversion rate multiplied by the USDC/USD price read from the NAV's own primary feed.

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

{% hint style="info" %}
**Mainnet feed.** The L2 deployments use a different oracle composition — see [syrupUSDC (L2)](/funds/infrastructure/onchain-accounting/price-feeds/syrupusdc-l2).
{% endhint %}

{% hint style="info" %}
The USDC/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Approach

`syrupUSDC.convertToAssets(1 syrupUSDC)` × USDC/USD (from the NAV's primary feed via `getPriceDataNoDivergence(USDC)`)

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-2df9394bbfcdb28c83e2d841e8dea6c19cc4f394%2Fprice-syrupusdc.svg?alt=media" alt="Left-to-right flowchart of the syrupUSDC (Mainnet) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>syrupUSDC (Mainnet) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = syrupUSDC.decimals()                       // vault share decimals
rate     = syrupUSDC.convertToAssets(10^decimals)     // USDC per 1 syrupUSDC (vault decimals)
price    = rate × USDC_USD / 10^decimals
```

| Input               | Source                                              | Description                                                     |
| ------------------- | --------------------------------------------------- | --------------------------------------------------------------- |
| `convertToAssets()` | `IERC4626(syrupUSDC)`                               | USDC backing per 1 syrupUSDC at the current vault exchange rate |
| `USDC_USD`          | NAV base — `getPriceDataNoDivergence(USDC)` (8 dec) | the NAV's primary USDC/USD price                                |

Vault decimals are read once at deploy. Result carries the feed's **8 decimals**.

***

## Constructor

```solidity
constructor(
    address syrupUsdc_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `syrupUsdc_` | The syrupUSDC vault address (also used to read vault decimals)                                                                              |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                             |
| `baseAsset_` | The registered base stablecoin whose USD price is read (USDC); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the base USD price is `≤ 0`, or if the NAV reports the base asset (USDC) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected USDC feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Ethereum mainnet** only. L2 deployments use [syrupUSDC (L2)](/funds/infrastructure/onchain-accounting/price-feeds/syrupusdc-l2), which relies on a Chainlink syrupUSDC/USDC feed instead.


# RWIV

Custom price feed for the Nexus Mutual Real-World Investments Vault (RWIV).

Custom price feed for the **Nexus Mutual Real-World Investments Vault (RWIV)**. RWIV is an ERC-4626 vault share backed by USDC. The price is derived from the vault's current share-to-asset conversion rate multiplied by the USDC/USD price read from the NAV's own primary feed.

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

{% hint style="info" %}
The USDC/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Approach

`RWIV.convertToAssets(1 RWIV)` × USDC/USD (from the NAV's primary feed via `getPriceDataNoDivergence(USDC)`)

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-53b2f5c1f3450a5374421ab40b481419991e7e09%2Fprice-rwiv.svg?alt=media" alt="Left-to-right flowchart of the RWIV price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>RWIV price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = RWIV.decimals()                        // vault share decimals
rate     = RWIV.convertToAssets(10^decimals)       // USDC per 1 RWIV (vault decimals)
price    = rate × USDC_USD / 10^decimals
```

| Input               | Source                                              | Description                                             |
| ------------------- | --------------------------------------------------- | ------------------------------------------------------- |
| `convertToAssets()` | `IERC4626(RWIV)`                                    | USDC backing per 1 RWIV share at the current vault rate |
| `USDC_USD`          | NAV base — `getPriceDataNoDivergence(USDC)` (8 dec) | the NAV's primary USDC/USD price                        |

The ERC-4626 `convertToAssets` call returns the current exchange rate. The vault decimals are read once at deploy. Result carries the feed's **8 decimals**. The USDC/USD base term is read from the NAV's primary feed rather than a hardcoded oracle.

***

## Constructor

```solidity
constructor(
    address rwiv_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `rwiv_`      | The RWIV vault address (also used to read vault decimals)                                                                                   |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                             |
| `baseAsset_` | The registered base stablecoin whose USD price is read (USDC); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the base USD price is `≤ 0`, or if the NAV reports the base asset (USDC) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected USDC feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Ethereum mainnet** only.


# sXDAI

Custom price feed for Savings xDAI (sDAI) on Gnosis Chain.

Custom price feed for **Savings xDAI (sDAI)** on Gnosis Chain. sXDAI is an ERC-4626 vault share that accrues yield from the Gnosis Chain savings rate. The price is derived from the vault's current share-to-xDAI conversion rate multiplied by the xDAI/USD price read from the NAV's own primary feed.

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

{% hint style="info" %}
The xDAI/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Approach

`sDAI.convertToAssets(1 sDAI)` × xDAI/USD (from the NAV's primary feed via `getPriceDataNoDivergence(xDAI)`)

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-336a73b78bec7d2d44491c18709c028a6942b7fd%2Fprice-sxdai.svg?alt=media" alt="Left-to-right flowchart of the sXDAI price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>sXDAI price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
decimals = sDAI.decimals()                       // vault share decimals
rate     = sDAI.convertToAssets(10^decimals)     // xDAI per 1 sDAI (vault decimals)
price    = xDAI_USD × rate / 10^decimals
```

| Input               | Source                                              | Description                                         |
| ------------------- | --------------------------------------------------- | --------------------------------------------------- |
| `convertToAssets()` | `IERC4626(sDAI)`                                    | xDAI backing per 1 sDAI at the current savings rate |
| `xDAI_USD`          | NAV base — `getPriceDataNoDivergence(xDAI)` (8 dec) | the NAV's primary xDAI/USD price                    |

xDAI on Gnosis Chain is the native gas token, pegged to DAI. The Gnosis savings rate accumulates directly in the sDAI vault, so `convertToAssets` reflects real-time interest accrual. Vault decimals are read once at deploy. Result carries the feed's **8 decimals**.

***

## Constructor

```solidity
constructor(
    address sDai_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `sDai_`      | The Savings xDAI vault address (also used to read vault decimals)                                                                           |
| `nav_`       | NAVCalculator (proxy) — supplies the base stablecoin's USD price via `getPriceDataNoDivergence`                                             |
| `baseAsset_` | The registered base stablecoin whose USD price is read (xDAI); **must equal the vault's `asset()`** — reverts `BaseAssetMismatch` otherwise |

***

## Staleness

The price is stale if the base USD price is `≤ 0`, or if the NAV reports the base asset (xDAI) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected xDAI feed. The `convertToAssets` rate has no heartbeat — it is a live view on the ERC-4626 vault.

***

## Chains

Deployed on **Gnosis** only. There is no Ethereum counterpart in this system: mainnet registers `DAI` but not `sDAI`, so no sDAI feed is configured there.


# stETH, lsETH & OETH (CrossRate)

Generic custom price feed for an asset Y that has no direct Y/USD feed but is quoted in a base asset X that does.

Generic custom price feed for an asset **Y** that has no direct Y/USD feed but is quoted in a base asset **X** that does. The price is the cross-rate of the NAV's own X/USD base price and a Chainlink Y/X feed. The shared `CrossRateCustomPriceFeed` base is instantiated as one **named per-token contract** for each asset that needs it. On Ethereum mainnet the **primary** instances are **lsETH** (`lsETH_CustomPriceFeed`, Liquid Collective — Y/X is Chainlink's *LsETH/ETH Exchange Rate*) and **OETH** (`OETH_CustomPriceFeed`) — each `Y/USD = ETH/USD × Y/ETH`, where the cross-rate is the asset's sole pricing feed.

{% hint style="info" %}
**stETH is not priced by a cross-rate.** Its primary is a [fundamental](/funds/infrastructure/onchain-accounting/price-feeds#custom-price-feeds) unit-rate feed (`1.0 × ETH/USD`), matching wstETH and eETH; the market `stETH/ETH` cross-rate is registered as a **monitor** (`stETHCrossRate`), deployed from the generic base rather than a named per-token subclass. This page describes how that monitor computes its price — it does not set stETH's NAV value.
{% endhint %}

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

The same base also backs **divergence monitors** for the fundamental LST/LRT primaries — **ezETH**, **ETHx**, **osETH**, and **rETH**, where the `Y/ETH` leg is an independent market feed. Three of the four have a named per-token subclass (`ezETH_CrossRateCustomPriceFeed`, `ETHx_CrossRateCustomPriceFeed`, `osETH_CrossRateCustomPriceFeed`); **rETH's monitor is deployed from the generic base** under the config key `rETHCrossRate`, as stETH's is. Those assets are priced by a fundamental protocol-rate primary ([ezETH](/funds/infrastructure/onchain-accounting/price-feeds/ezeth), [ETHx](/funds/infrastructure/onchain-accounting/price-feeds/ethx), [osETH](/funds/infrastructure/onchain-accounting/price-feeds/ostoken), [rETH](/funds/infrastructure/onchain-accounting/price-feeds/reth)); the cross-rate is registered as a [monitor feed](/funds/infrastructure/onchain-accounting/price-feeds#primary-feed-and-divergence-monitors), so a market depeg makes it diverge from the fundamental primary and NAV flags the asset `irregular` — it never prices NAV. The L2 deployments use the same pattern (e.g. `weETHCrossRate`, `rsETHCrossRate` monitors on Arbitrum/Base).

***

## Approach

NAV base X/USD (`getPriceDataNoDivergence(WETH)`) × Chainlink Y/X

{% hint style="info" %}
The base/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-443d78aad8c1f9874eb87fccb9b9220b6ff763c7%2Fprice-cross-rate.svg?alt=media" alt="Left-to-right flowchart of the stETH, lsETH &#x26; OETH (CrossRate) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>stETH, lsETH &#x26; OETH (CrossRate) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = (X/USD) × (Y/X) / 10^(Y/X decimals)
```

| Input   | Source                                              | Description                     |
| ------- | --------------------------------------------------- | ------------------------------- |
| `X/USD` | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary ETH/USD price |
| `Y/X`   | Chainlink Y/X feed (`latestRoundData`)              | Underlying Y quoted in X        |

The Y/X leg is a Chainlink feed; the X/USD (ETH/USD) leg is the NAV's primary base price. The divisor is `10 ** yToX.decimals()`, which cancels the Y/X feed's decimals so the result keeps the X/USD leg's scale. Output is **8 decimals** (inherited from the X/USD leg via `_decimals`).

For the mainnet instances, X is ETH (ETH/USD via the NAV base) and Y is stETH, lsETH, or OETH respectively (a stETH/ETH, LsETH/ETH, or OETH/ETH feed).

***

## Constructor

```solidity
constructor(
    address underlyingAsset_,
    address yToXFeed_,
    uint256 yToXHeartbeat_,
    address nav_,
    address baseAsset_
)
```

| Parameter          | Description                                                                                |
| ------------------ | ------------------------------------------------------------------------------------------ |
| `underlyingAsset_` | The asset Y whose USD price is returned (stETH, lsETH, or OETH)                            |
| `yToXFeed_`        | Chainlink Y/X aggregator (e.g. stETH/ETH or OETH/ETH)                                      |
| `yToXHeartbeat_`   | Staleness window (seconds) for the Y/X feed                                                |
| `nav_`             | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`       | The registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

The Y/X heartbeat is checked directly. `getLatestPrice()` returns `stale = true` if the Y/X feed satisfies `block.timestamp − updatedAt ≥ heartbeat`, if either leg's answer is `≤ 0`, if the Y/X `updatedAt` is in the future (clamped to `block.timestamp`), or if the NAV reports the base asset (WETH) primary feed stale. The base leg's `updatedAt` comes from the NAV's selected feed. The reported `updatedAt` is the **older** of the two legs.

***

## Chains

On **Ethereum**, as **primary** feeds: `lsETH` and `OETH`, each configured with the matching Y/X feed. As **monitor** feeds (same base contract, market Y/ETH leg), watching their fundamental primaries: `stETHCrossRate`, `ezETHCrossRate`, `ezETHCrossRateMarket`, `ETHxCrossRate`, `osETHCrossRate`, and `rETHCrossRate` — [ezETH carries two](/funds/infrastructure/onchain-accounting/price-feeds/ezeth) because its original monitor's rate leg stopped being a market feed.

On the L2s, all monitors: `weETHCrossRate`, `ezETHCrossRate` and `rsETHCrossRate` on **Arbitrum**, and `weETHCrossRate` on **Base**. Gnosis has no cross-rate instance.

Every one of these takes an external `yToXFeed`, so each is covered by the pre-deploy [rate-feed guard](/funds/infrastructure/onchain-accounting/price-feeds#custom-price-feeds) — pair identity where the provider's `description()` names the pair, and a declared `$UNVERIFIABLE` exemption plus a plausibility band where it does not.


# LDO

Custom price feed for the Lido DAO governance token (LDO).

Custom price feed for the **Lido DAO governance token (LDO)**. No direct LDO/USD feed exists, so the price is composed from a Chainlink LDO/ETH feed and the NAV's own ETH/USD base price.

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

***

## Approach

Chainlink LDO/ETH × NAV base ETH/USD (`getPriceDataNoDivergence(WETH)`)

{% hint style="info" %}
The base/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-27d9047108a4838877808216021032179b790166%2Fprice-ldo.svg?alt=media" alt="Left-to-right flowchart of the LDO price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>LDO price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = (LDO/ETH) × (ETH/USD) / 10^(LDO/ETH decimals)
```

| Input     | Source                                              | Description                     |
| --------- | --------------------------------------------------- | ------------------------------- |
| `LDO/ETH` | Chainlink LDO/ETH feed (`latestRoundData`)          | LDO price in ETH (18 dec)       |
| `ETH/USD` | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary ETH/USD price |

The LDO/ETH leg is a Chainlink feed; the ETH/USD leg is the NAV's primary base price. The divisor is `10 ** ldoToEth.decimals()`. Output is **8 decimals** (inherited from the ETH/USD leg via `_decimals`).

***

## Constructor

```solidity
constructor(
    address ldo_,
    address ldoToEthChainlinkAddress_,
    uint256 ldoToEthChainlinkHeartbeat_,
    address nav_,
    address baseAsset_
)
```

| Parameter                     | Description                                                                                |
| ----------------------------- | ------------------------------------------------------------------------------------------ |
| `ldo_`                        | LDO token address (the supported underlying asset)                                         |
| `ldoToEthChainlinkAddress_`   | Chainlink LDO/ETH aggregator                                                               |
| `ldoToEthChainlinkHeartbeat_` | Staleness window (seconds) for the LDO/ETH feed                                            |
| `nav_`                        | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`                  | The registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

The LDO/ETH heartbeat is checked directly. `getLatestPrice()` returns `stale = true` if the LDO/ETH feed satisfies `block.timestamp − updatedAt ≥ heartbeat`, if either leg's answer is `≤ 0`, if the LDO/ETH `updatedAt` is in the future (clamped to `block.timestamp`), or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected feed. The reported `updatedAt` is the **older** of the two legs.

***

## Chains

Deployed on **Ethereum**.


# ETH+

Custom price feed for Reserve Protocol's ETH+ RToken on Ethereum.

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#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="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-6554b48493cdd57e0a9df9fd536143258b940b5f%2Fprice-eth-plus.svg?alt=media" 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**.


# NXM / wNXM

Custom price feed for Nexus Mutual's NXM token and its 1:1 transferable wrapper wNXM on Ethereum.

Custom price feed for **Nexus Mutual's NXM** token and its 1:1 transferable wrapper **wNXM** on Ethereum. NXM has no external market oracle by design; its value is set internally by Nexus Mutual's Ratcheting AMM (RAMM), combined here with an ETH/USD price read from the NAV's own primary feed.

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

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

***

## Approach

`RAMM.getInternalPrice()` (ETH per NXM, TWAP) × NAV-selected ETH/USD.

Deploy **one instance per priced token**: one for NXM and one for wNXM. Both use the same RAMM book value per NXM-unit, since a member can unwrap wNXM 1:1 to NXM.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-18da6f4b44c5cf09f40ac2cb7542bd6b0a0f7faa%2Fprice-nxm.svg?alt=media" alt="Left-to-right flowchart of the NXM / wNXM price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>NXM / wNXM price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
price = getInternalPrice × ETH_USD / 1e18
```

| Input                | Source                                              | Description                                         |
| -------------------- | --------------------------------------------------- | --------------------------------------------------- |
| `getInternalPrice()` | `IRamm(RAMM)`                                       | Manipulation-resistant TWAP of ETH per NXM (18 dec) |
| `ETH_USD`            | NAV base — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price                    |

`getInternalPrice()` is the RAMM's internal ETH-per-NXM price (18 dec); multiplying by the 8-dec ETH/USD and dividing by `1e18` yields an **8-decimal** USD price. If the internal price is zero, the feed returns stale.

***

## Constructor

```solidity
constructor(
    address token_,
    address ramm_,
    address nav_,
    address baseAsset_
)
```

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| `token_`     | The priced token: NXM or its 1:1 wrapper wNXM                                              |
| `ramm_`      | Nexus Mutual RAMM contract providing the internal ETH-per-NXM price                        |
| `nav_`       | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_` | The registered base asset whose USD price is read (WETH)                                   |

The constructor reverts if `RAMM.getInternalPrice()` returns 0 at deploy, failing fast on a wrong or non-contract RAMM address.

***

## Staleness

The price is stale if `getInternalPrice()` returns 0, or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` comes from the NAV's selected WETH feed. The RAMM internal price itself has no heartbeat — it is a view function read live on each call.

***

## Chains

Deployed on **Ethereum mainnet**. One instance is deployed per priced token (NXM and wNXM).


# wstETH (L2)

Custom price feed for Lido Wrapped Staked ETH on L2 chains.

Custom price feed for **Lido Wrapped Staked ETH** on L2 chains. No direct wstETH/USD feed exists, so the price is composed from a Chainlink wstETH/ETH feed and a Chainlink ETH/USD feed.

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

{% hint style="info" %}
**L2 feed.** For the Ethereum mainnet feed, see [wstETH](/funds/infrastructure/onchain-accounting/price-feeds/wsteth).
{% endhint %}

***

## Approach

Chainlink wstETH/ETH × ETH/USD — the ETH/USD term is read from the NAV's own primary feed via `getPriceData(WETH)`, not a hardcoded oracle.

<figure><img src="https://1699303810-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUsQM4bzcmOPSgkWddkeN%2Fuploads%2Fgit-blob-eec5b0d86fa4a40225b59c0c5f8eacbf934b2c21%2Fprice-wsteth-l2.svg?alt=media" alt="Left-to-right flowchart of the wstETH (L2) price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>wstETH (L2) price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

{% hint style="info" %}
The ETH/USD term is sourced from the NAV's own selector — see [Base/USD from the NAV's own selector](/funds/infrastructure/onchain-accounting/price-feeds#base-usd-from-the-navs-own-selector).
{% endhint %}

***

## Price calculation

```
price = (wstETH/ETH) × (ETH/USD, NAV-selected) / 10^(wstETH/ETH decimals)
```

| Input        | Source                                                  | Description                      |
| ------------ | ------------------------------------------------------- | -------------------------------- |
| `wstETH/ETH` | Chainlink wstETH/ETH feed (`latestRoundData`)           | wstETH price in ETH (18 dec)     |
| `ETH/USD`    | **NAV base** — `getPriceDataNoDivergence(WETH)` (8 dec) | the NAV's primary WETH/USD price |

The wstETH/ETH feed already embeds the Lido exchange rate, so no on-chain rate call is made. The divisor is `10 ** RATE_FEED.decimals()`, captured once at construction as `_rateUnit`. Output is **8 decimals** (inherited from the ETH/USD term via `_decimals`).

***

## Constructor

```solidity
constructor(
    address wstEth_,
    address wstEthToEthChainlink_,
    uint256 wstEthToEthHeartbeat_,
    address nav_,
    address baseAsset_
)
```

| Parameter               | Description                                                                                |
| ----------------------- | ------------------------------------------------------------------------------------------ |
| `wstEth_`               | wstETH token address (the supported underlying asset)                                      |
| `wstEthToEthChainlink_` | Chainlink wstETH/ETH aggregator                                                            |
| `wstEthToEthHeartbeat_` | Staleness window (seconds) for the wstETH/ETH feed                                         |
| `nav_`                  | NAVCalculator (proxy) — supplies the base asset's USD price via `getPriceDataNoDivergence` |
| `baseAsset_`            | the registered base asset whose USD price is read (WETH)                                   |

***

## Staleness

`getLatestPrice()` returns `stale = true` if the wstETH/ETH feed satisfies `block.timestamp − updatedAt ≥ heartbeat`, if its answer is `≤ 0`, if its `updatedAt` is in the future (clamped to `block.timestamp` before the check), or if the NAV reports the base asset (WETH) stale — all its feeds stale. The base leg's `updatedAt` now comes from the NAV's selected WETH feed. The reported `updatedAt` is the **older** (smaller) of the two legs.

***

## Chains

Deployed on **Arbitrum, Base, and Gnosis**. The Ethereum mainnet counterpart is [wstETH (Mainnet)](/funds/infrastructure/onchain-accounting/price-feeds/wsteth), which derives the rate from `stEthPerToken()` instead of a wstETH/ETH feed.




---

[Next Page](/llms-full.txt/1)

