> 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-safety-module.md).

# Aave Safety Module

Reports an account's stake in the legacy Aave V3 Safety Module across a hardcoded set of stake tokens. For each stake token it splits the staking principal into a locked and an (optionally present) unlocked leg by `isLocked`, plus a claimable-rewards leg. The stake-token set is small and slow-moving and Aave publishes no on-chain registry, so the addresses are pinned as compile-time constants.

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

{% hint style="info" %}
Mainnet only. The legacy Safety Module is an Ethereum-mainnet protocol. Adding a new SM stake token is a source-code change (new constant + record), not an admin transaction.
{% endhint %}

***

## Hardcoded stake tokens

| Stake token                             | Address                                      | Staked asset                                          | Reward |
| --------------------------------------- | -------------------------------------------- | ----------------------------------------------------- | ------ |
| stkAAVE                                 | `0x4da27a545c0c5B758a6BA100e3a049001de870f5` | AAVE                                                  | AAVE   |
| stkABPT (Balancer V1 BPT)               | `0xa1116930326D21fB917d5A27F1E9943A9595fb47` | ABPT `0x41A08648C3766F9F9d85598fF102a08f4ef84F84`     | AAVE   |
| stkABPTv2 (Balancer V2 AAVE/wstETH BPT) | `0x9eDA81C21C273a82BE9Bbc19B6A6182212068101` | ABPT\_V2 `0x3de27EFa2F1AA663Ae5D458857e731c129069F29` | AAVE   |
| stkGHO                                  | `0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d` | GHO `0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f`      | AAVE   |

AAVE = `0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9`. Reward token is AAVE for every entry.

***

## Positions returned

Up to three legs per stake token (whose staked/reward asset is registered on NAVCalculator):

| Leg              | PositionKind | isDebt  | isLocked | Description                                                                                                                                                                                                       |
| ---------------- | ------------ | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Locked staking   | `Staking`    | `false` | `true`   | Staked-token amount that is **not** withdrawable right now — active stake plus any cooldown that is still cooling or whose claim window has already passed. `amount = previewRedeem(lockedShares)`                |
| Unlocked staking | `Staking`    | `false` | `false`  | Staked-token amount that **is** withdrawable right now. `amount = previewRedeem(unlockedShares)`. Present only when there is an active cooldown whose period has elapsed and is still inside the `UNSTAKE_WINDOW` |
| Rewards          | `Rewards`    | `false` | `false`  | Total claimable (accrued, unclaimed) reward tokens. `amount = getTotalRewardsBalance(account)`, denominated in AAVE                                                                                               |

Zero-amount legs are skipped. For stkAAVE all legs are denominated in AAVE and are told apart by `PositionKind` (rewards vs. staking) and by `isLocked` (locked vs. unlocked staking).

***

## Balance calculation

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

Per stake token, the balance is split into a locked and an unlocked staking leg by reading the account's cooldown:

```
(timestamp, amount) = stakeToken.stakersCooldowns(account)
cooldownSeconds     = stakeToken.getCooldownSeconds()
window              = stakeToken.UNSTAKE_WINDOW()

inWindow = now >= timestamp + cooldownSeconds
        && now <= timestamp + cooldownSeconds + window   // cooldown elapsed, still claimable

unlockedShares = inWindow ? <shares the cooldown covers> : 0
lockedShares   = totalShares - unlockedShares

lockedAmount   = stakeToken.previewRedeem(lockedShares)     // isLocked = true
unlockedAmount = stakeToken.previewRedeem(unlockedShares)   // isLocked = false, only if inWindow
rewards        = stakeToken.getTotalRewardsBalance(account) // reward-token (AAVE) units, isLocked = false
```

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

***

## 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 `["Safety Module"]` (the category is itself the leaf), surfaced only by the verbose reads.

***

## Constructor

```solidity
constructor(address navCalculator)
```

| Parameter       | Description                                                                                     |
| --------------- | ----------------------------------------------------------------------------------------------- |
| `navCalculator` | NAVCalculator address (non-zero, must be a contract); used to resolve registered-asset metadata |

No stake-token addresses are passed — they are baked into bytecode.

***

## 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-safety-module.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.
