> 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/balance-adapters/aave-umbrella.md).

# Aave Umbrella

Reports an account's stake in Aave Umbrella across every stake token registered with a single `RewardsController`. For each stake position it splits the staking principal into a locked and an (optionally present) unlocked leg by `isLocked`, plus a claimable-rewards leg, all denominated in the bare reserve token (e.g. USDC, USDT, WETH, GHO). The stake-token set is discovered dynamically at runtime — nothing but the `RewardsController` is pinned.

* **Type:** Plain adapter
* **protocolSubId:** `keccak256("aave-v3-umbrella")` (brand `aave`, ecosystem `aave-v3`)
* **Source:** [`AaveUmbrellaBalanceAdapter.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/balances/BalanceAdapters/AaveUmbrellaBalanceAdapter.sol)

{% hint style="info" %}
Aave governance can add a new Umbrella stake token (or change its reward token) and this adapter picks it up automatically, provided the bare reserve is registered on NAVCalculator. Unregistered reserves are silently skipped.
{% endhint %}

***

## Positions returned

Up to three legs per discovered stake token (whose bare reserve is registered on NAVCalculator):

| Leg              | PositionKind | isDebt  | isLocked | Description                                                                                                                                                                                              |
| ---------------- | ------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Locked staking   | `Staking`    | `false` | `true`   | Stake principal that is **not** withdrawable right now — active stake plus any cooldown still cooling or whose withdrawal window has already passed. Denominated in bare-reserve units                   |
| Unlocked staking | `Staking`    | `false` | `false`  | Stake principal that **is** withdrawable right now. Present only when there is an active cooldown whose period has elapsed and is still inside its `withdrawalWindow`. Denominated in bare-reserve units |
| Rewards          | `Rewards`    | `false` | `false`  | Claimable unclaimed rewards, mapped to bare-reserve units                                                                                                                                                |

Zero-amount legs are skipped. All staking amounts are denominated in the bare reserve (e.g. USDC, USDT, WETH, GHO), resolved through at most one ERC-4626 wrapper hop.

***

## Balance calculation

<figure><img src="/files/oaJOb5RGoBAObUKrySrM" alt="Flowchart of the Aave Umbrella balance adapter: read calls derive position legs into PositionBalance entries."><figcaption><p>Aave Umbrella adapter — how the underlying balances reported to the NAV Calculator are derived.</p></figcaption></figure>

Stake tokens are enumerated via `RewardsController.getAllAssets()`. Each stake token's bare reserve is resolved by following at most one ERC-4626 wrapper hop on the `asset()` chain (`stk → waToken → bare reserve`, or `stk → bare reserve` directly).

**Staking principal** — convert shares through the wrapper chain, then split by withdrawability:

```
amount = stakeToken.convertToAssets(stakeToken.balanceOf(account))
firstLevel = stakeToken.asset()
if firstLevel is an ERC-4626 wrapper (waToken):
    amount = firstLevel.previewRedeem(amount)   // one extra hop to the bare reserve; 0 if unreadable, no fallback
```

The inner waToken hop uses `previewRedeem` so a redemption fee on the wrapper (possible via Aave governance) is not over-reported (`convertToAssets` ignores it per EIP-4626 §4.3). There is **no `convertToAssets` fallback**: since §4.3 makes `convertToAssets ≥ previewRedeem` by construction, falling back could only ever report more than a redemption would pay, so an unreadable `previewRedeem` reports **`0`** instead. A read that runs out of gas fails closed with `RedeemValueGasExhausted(vault, stipend, consumed)` rather than reporting `0` — see [a starved read is not an empty read](/funds/infrastructure/onchain-accounting/balance-adapters.md#how-an-adapter-is-built). Because Umbrella slashing reduces `totalAssets` while keeping `totalSupply` constant, the walk is automatically post-slash-correct.

The resulting principal is split into a locked and an (optionally present) unlocked leg by reading the account's cooldown:

```
(amount, endOfCooldown, withdrawalWindow) = stakeToken.getStakerCooldown(account)
                                            // withdrawalWindow is snapshotted per cooldown

inWindow = now >= endOfCooldown
        && now <= endOfCooldown + withdrawalWindow   // cooldown elapsed, still claimable

unlocked = inWindow ? <bare-reserve amount the cooldown covers> : 0   // isLocked = false
locked   = principal - unlocked                                       // isLocked = true
```

If the cooldown reads fail, or there is no active cooldown, the whole principal is treated as locked and no unlocked leg is emitted.

**Rewards** — pulled from `calculateCurrentUserRewards(stakeToken, user)`; each reward token is resolved to its bare reserve via `IAToken.UNDERLYING_ASSET_ADDRESS()` (or treated as already-bare), and amounts whose bare reserve matches the stake's bare reserve are summed. Reward aTokens are 1:1 unit-equivalent with their underlying reserve.

External probes (`asset()`, `convertToAssets`, `getStakerCooldown`, `UNDERLYING_ASSET_ADDRESS()`) use low-level staticcalls with a ≤30k gas budget so a misbehaving fallback cannot drain the read.

***

## Identity

* **positionId:** `abi.encode(address stakeToken)` — the same value for all three legs of a stake token; there is no discriminator in the `positionId`
* **positionKind:** `Staking` · `Rewards`

The two staking legs share the same `positionId` and `positionKind`; since `isLocked` is a mutable per-leg attribute and **not** part of the key, they reconcile into **one coordinate** and sum to the total staked (read `isLocked` per leg — `true` locked, `false` unlocked — for the breakdown). The rewards leg shares the same `positionId` but is told apart from staking by its `positionKind` (`Rewards`). There is no `labels` field on the position; the adapter implements `positionLabels(positionId)`, which returns `["Umbrella Module"]` (the category is itself the leaf), surfaced only by the verbose reads. `protocolName()` returns **`"Aave V3"`**, not `"Aave Umbrella"` — the [display name is the protocol, never the product](/funds/infrastructure/onchain-accounting/balance-adapters.md#how-an-adapter-is-built), and Umbrella already carries its product in the label. The rendered breadcrumb is therefore `["Aave V3", "Umbrella Module"]`, which groups Umbrella with its Aave V3 siblings (lending, Safety Module) instead of splitting it out as a phantom sibling protocol.

***

## Constructor

```solidity
constructor(address rewardsController, address navCalculator)
```

| Parameter           | Description                                                                                           |
| ------------------- | ----------------------------------------------------------------------------------------------------- |
| `rewardsController` | Umbrella RewardsController (non-zero, must be a contract); the trust anchor for stake-token discovery |
| `navCalculator`     | NAVCalculator address (non-zero, must be a contract); used to resolve registered-asset metadata       |

***

## Registration

Registered as a plain adapter with `addBalanceAdapters([adapter])` (MANAGER-gated); removed with `removeBalanceAdapters`.


---

# 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/balance-adapters/aave-umbrella.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.
