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

# Meta balance adapters

A **meta-adapter** is a single immutable contract that reports positions for **many instances of one protocol** — every Morpho vault, every Aave market, every Balancer pool — instead of one deployed adapter per instance. It is the model used for all high-cardinality protocols.

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

***

## Why

The original model deployed **one adapter contract per pool / market / vault**: covering N Morpho vaults meant N immutable contracts, each a CREATE deploy plus a governance registration. For protocols with many fungible instances this scaled badly — every new pool was a deploy, an audit-surface delta, and a governance action.

The framework's core rule is that **adapters are immutable** — no `Ownable`, no admin functions. So the instance set can't live (mutably) inside the adapter. Instead:

{% hint style="info" %}
The adapter stays a pure function of `(account, instance)` over *any* instance of its protocol. The governed **"which instances do we actually query"** set lives on the `NAVCalculator` — which is already upgradeable and `MANAGER`-gated — and is passed to the adapter at query time.
{% endhint %}

This keeps every meta-adapter immutable while making the instance set a governed, on-chain registry.

***

## How it works

* **`IMetaBalanceAdapter`** extends `IBalanceAdapter` and is keyed by a `bytes32` **instance coordinate**. Its inherited `getAdapterPositions(account, assetFilter)` reads the adapter's own NAV-configured set via `NAV_CALCULATOR.getMetaInstances(address(this))` and fans out over it — so the NAV Calculator's read path is **unchanged** (no meta-specific branch).
* **`MetaBalanceAdapter`** is the abstract base. It provides ERC-165, the registered-asset helper, and per-instance fan-out with **per-instance revert isolation** — a single bad/unknown instance coordinate is caught and skipped, and cannot zero out the rest of the batch. An instance that instead runs **out of gas** is *not* absorbed: the fan-out raises `InstanceGasExhausted(adapter, instance, stipend, consumed)`, because recording a starved instance as an empty leg list would drop its debt leg and over-report NAV behind a complete-looking answer. See [a starved read is not an empty read](/funds/infrastructure/onchain-accounting/balance-adapters.md#how-an-adapter-is-built). Concrete adapters implement two hooks (`_instancePositions`, `_instanceAssets`) and optionally `_instancePositionId`.
* **Deploy-time compatibility check.** The base constructor probes the NAVCalculator it is given for `getRegisteredAsset` and reverts `NAVCalculatorTooOld(navCalculator)` if it cannot serve it — meta-adapters rely on that O(1) lookup, and pointing one at an older implementation would otherwise fail later, at read time. The guard binds the **next** adapter deployed, not those already live.
* **Instance coordinate (`bytes32`).** For most protocols this is an **address widened to `bytes32`** (a vault, pool, gauge, or data-provider address). For protocols whose native identifier is already 32 bytes it is used **raw** — Morpho market ids and Balancer V2 pool ids.
* **Identity is derived on-chain** from the coordinate: `positionId = abi.encode(vault)` and the 3-level taxonomy (`protocolBrand` / `protocolId` / `protocolSubId`) set from `ProtocolIds.sol`. Display labels are produced lazily — the adapter's `positionLabels(positionId)` derives the breadcrumb (e.g. from the vault's ERC-20 `name()` or the pool's coin symbols) on the verbose read path only. Nothing instance-specific is stored in config — see [Position identity](/funds/infrastructure/onchain-accounting/concepts/position-identity.md).

***

## Registration

Meta-adapters are registered and curated by the security council Safe (`MANAGER` role) on the NAV Calculator. The adapter contract is immutable; the **instance set** is the governed, mutable part, with its own add/remove lifecycle:

| Action                                             | Call                                        |
| -------------------------------------------------- | ------------------------------------------- |
| **Register** the adapter + seed instances (atomic) | `addMetaBalanceAdapter(adapter, instances)` |
| **Add** instances (extend coverage)                | `addMetaInstances(adapter, instances)`      |
| **Remove** instances (drop coverage)               | `removeMetaInstances(adapter, instances)`   |
| **Remove** the adapter entirely                    | `removeBalanceAdapters([adapter])`          |
| **Inspect** the configured set                     | `getMetaInstances(adapter)` (view)          |

```solidity
// Register the meta-adapter and seed its instance set atomically.
function addMetaBalanceAdapter(address adapter, bytes32[] calldata instances) external;

// Adjust the queried set later (all-or-nothing; rejects duplicates / unknown coordinates).
function addMetaInstances(address adapter, bytes32[] calldata instances) external;
function removeMetaInstances(address adapter, bytes32[] calldata instances) external;

// Inspect the configured set.
function getMetaInstances(address adapter) external view returns (bytes32[] memory);
```

`addMetaInstances` reverts `DuplicateMetaInstance` on a coordinate already present or repeated in the batch (a duplicate would double-count NAV); `removeMetaInstances` reverts `MetaInstanceNotFound` if a coordinate isn't present; both revert `NotMetaAdapter` if the target isn't a meta-adapter, and on an empty list. All are all-or-nothing. Full detail: NAV Calculator [Admin / Manager API](/funds/infrastructure/onchain-accounting/contracts/nav-calculator.md#admin-manager-api).

Example — the configured instance set of the Morpho Vaults meta-adapter (illustrative; each `bytes32` is a vault address widened to 32 bytes):

```
getMetaInstances(0x4d9E…2FA7)   // MorphoVaultsMeta
→ [
    0x000000000000000000000000beef…0010,   // vault 0xBEEF…0010
    0x000000000000000000000000beef…01a4,   // vault 0xBEEF…01A4
    0x000000000000000000000000d63070…b15c   // vault 0xD630…B15C
  ]
```

For raw-`bytes32` adapters (Morpho Markets, Balancer V2 Pools) the coordinates are the native ids themselves, not widened addresses.

Adding a new pool/vault/market is therefore a single `addMetaInstances` transaction — **not** a contract deploy.

{% hint style="warning" %}
**Trust model.** `MANAGER` can add an arbitrary instance coordinate, but a malicious or incorrect instance can only mis-report through the adapter's own read path (e.g. a fake ERC-4626 that lies about `convertToAssets`); it cannot fabricate values out of band, and NAV still drops positions whose asset is unregistered. Instance curation is a governance responsibility — index → whitelist → `MANAGER` transaction — mirroring how price feeds and plain adapters are added.
{% endhint %}

{% hint style="info" %}
Plain (single-scope) adapters use `addBalanceAdapters` instead and have no instance set. See [Balance adapters](/funds/infrastructure/onchain-accounting/balance-adapters.md). A separate [assisted-adapter family](/funds/infrastructure/onchain-accounting/assisted-balance-adapters.md) (ether.fi exit, StakeWise exit, Nexus Mutual) caches position coordinates the owner feeds permissionlessly on-chain (`msg.sender`-keyed, no roles) — distinct from the meta model here.
{% endhint %}

***

## Meta-adapter catalogue

| Adapter                                                                                                              | Protocol       | Instance key              | Positions                                          |
| -------------------------------------------------------------------------------------------------------------------- | -------------- | ------------------------- | -------------------------------------------------- |
| [Aave V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/aave-v3.md)                                 | Aave V3        | Data provider (addr)      | Supply · Collateral · Borrow                       |
| [Morpho Markets](/funds/infrastructure/onchain-accounting/meta-balance-adapters/morpho-market.md)                    | Morpho Blue    | `bytes32` market id (raw) | Collateral · Supply · Borrow                       |
| [Morpho Vaults](/funds/infrastructure/onchain-accounting/meta-balance-adapters/morpho-vault.md)                      | Morpho         | Vault (addr)              | Vault shares → assets                              |
| [Gearbox Markets](/funds/infrastructure/onchain-accounting/meta-balance-adapters/gearbox.md)                         | Gearbox V3     | Farming pool (addr)       | Pool shares → assets                               |
| [Gearbox Credit Accounts](/funds/infrastructure/onchain-accounting/meta-balance-adapters/gearbox-credit-accounts.md) | Gearbox V3     | Credit Account (addr)     | Borrowed (debt) · Collateral                       |
| [Euler Vaults](/funds/infrastructure/onchain-accounting/meta-balance-adapters/euler-vaults.md)                       | Euler          | Vault (addr)              | Supply/Collateral · Borrow                         |
| [Fluid fTokens](/funds/infrastructure/onchain-accounting/meta-balance-adapters/fluid-ftokens.md)                     | Fluid          | fToken (addr)             | Supply → assets                                    |
| [Fluid Vaults](/funds/infrastructure/onchain-accounting/meta-balance-adapters/fluid-vaults.md)                       | Fluid          | Vault (addr)              | Collateral · Borrow                                |
| [Compound V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/compound-v3.md)                         | Compound V3    | Comet (addr)              | Supply · Borrow · Collateral · Rewards             |
| [StakeWise V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/stakewise-v3.md)                       | StakeWise      | Vault (addr)              | Staking · osToken debt                             |
| [Balancer V3 Pools](/funds/infrastructure/onchain-accounting/meta-balance-adapters/balancer-v3-pools.md)             | Balancer V3    | Pool (addr)               | Pro-rata pool tokens                               |
| [Balancer V3 Gauges](/funds/infrastructure/onchain-accounting/meta-balance-adapters/balancer-v3-gauges.md)           | Balancer V3    | Gauge (addr)              | Staked pool tokens · Rewards                       |
| [Balancer V2 Pools](/funds/infrastructure/onchain-accounting/meta-balance-adapters/balancer-v2-pools.md)             | Balancer V2    | `bytes32` pool id (raw)   | Pro-rata pool tokens                               |
| [Balancer V2 Gauges](/funds/infrastructure/onchain-accounting/meta-balance-adapters/balancer-v2-gauges.md)           | Balancer V2    | Gauge (addr)              | Staked pool tokens · Rewards                       |
| [Curve Pools](/funds/infrastructure/onchain-accounting/meta-balance-adapters/curve-pools.md)                         | Curve          | Pool (addr)               | Pro-rata pool coins                                |
| [Curve Gauges](/funds/infrastructure/onchain-accounting/meta-balance-adapters/curve-gauges.md)                       | Curve          | Gauge (addr)              | Staked coins · CRV + extra rewards                 |
| [Convex](/funds/infrastructure/onchain-accounting/meta-balance-adapters/convex.md)                                   | Convex         | Reward pool (addr)        | Staked coins · CRV/CVX + extras                    |
| [Uniswap V2](/funds/infrastructure/onchain-accounting/meta-balance-adapters/uniswap-v2.md)                           | Uniswap V2     | Pair (addr)               | Pro-rata pair tokens                               |
| [Uniswap V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/uniswap-v3.md)                           | Uniswap V3     | Pool (addr)               | In-range liquidity (Supplied) · Fees, per LP NFT   |
| [PancakeSwap V2](/funds/infrastructure/onchain-accounting/meta-balance-adapters/pancakeswap-v2.md)                   | PancakeSwap V2 | Pair (addr)               | Pro-rata pair tokens                               |
| [PancakeSwap V3](/funds/infrastructure/onchain-accounting/meta-balance-adapters/pancakeswap-v3.md)                   | PancakeSwap V3 | Pool (addr)               | In-range liquidity (Supplied) · Fees, per LP NFT   |
| [Symbiotic Vaults](/funds/infrastructure/onchain-accounting/balance-adapters/symbiotic-vaults.md)                    | Symbiotic      | Vault (addr)              | Vault shares redeemed to the underlying (Supplied) |

{% hint style="info" %}
The Uniswap V3 / PancakeSwap V3 meta-adapters discover an account's LP NFTs on-chain (the NonfungiblePositionManager is `ERC721Enumerable`) and value them per configured pool. They link the GPL-2.0-or-later Uniswap V3 math libraries, so those two contracts are `GPL-2.0-or-later` rather than the repo's default BUSL-1.1.
{% endhint %}


---

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

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

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

```
GET https://docs.kpk.io/funds/infrastructure/onchain-accounting/meta-balance-adapters.md?ask=<question>&goal=<endgoal>
```

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

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

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