> 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/stakewise-v3.md).

# StakeWise V3

One immutable meta-adapter that reports an account's StakeWise V3 vault positions across any vault on a chain. Per vault it emits two gross legs: a `Staking` leg (vault shares converted to the underlying) and a `Borrowed` leg (minted osToken debt converted to the underlying).

* **Type:** Meta-adapter
* **protocolSubId:** `keccak256("stakewise-v3-vaults")`
* **Instance key:** the StakeWise V3 vault address, widened to `bytes32`.
* **Source:** [`StakeWiseV3VaultsMetaBalanceAdapter.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/balances/MetaBalanceAdapters/StakeWiseV3VaultsMetaBalanceAdapter.sol)

***

## Positions returned

Up to two legs per vault, both denominated in the underlying staking asset:

| Leg          | PositionKind | isDebt  | Description                                                       |
| ------------ | ------------ | ------- | ----------------------------------------------------------------- |
| Staking      | `Staking`    | `false` | Vault shares converted to the underlying staking asset            |
| osToken debt | `Borrowed`   | `true`  | Minted osToken position converted to the underlying staking asset |

Both legs are dropped when their amount is zero. The whole instance returns empty if the underlying is unregistered in NAVCalculator or filtered out by `assetFilter`.

***

## Balance calculation

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

Both legs are denominated in `UNDERLYING_TOKEN` (the chain-level staking asset, e.g. the ETH sentinel on mainnet, GNO on Gnosis):

```
vaultAssets = vault.convertToAssets(vault.getShares(account))                            // Staking
mintedOs    = OS_TOKEN_VAULT_CONTROLLER.convertToAssets(vault.osTokenPositions(account))  // Borrowed
```

The osToken debt is denominated in osToken **shares**, whose exchange rate is governed by the `OsTokenVaultController` — a different object from the staking vault's own share→asset rate. The `Borrowed` leg therefore converts through the controller (the same rate object the osToken price feed uses); converting through the vault's rate would mis-scale the debt.

The two legs are gross (not netted): the staking principal and the osToken debt are reported separately.

***

## Identity

* **positionId:** `abi.encode(address vault)`
* **positionKind:** `Staking` (vault shares), `Borrowed` (osToken debt)
* **Labels:** the adapter implements `positionLabels(positionId)`, returning a two-tier leaf — both tiers read live and fail-open, surfaced only on the verbose read path:

  1. the vault's ERC20 `name()` — `["Vault", "Stakers Union GNO"]`. Only StakeWise's `…Erc20Vault` implementation variants are ERC20s and answer it;
  2. otherwise `"<underlying symbol> <vault address>"` — `["Vault", "GNO 0x4b4406ed…"]`, where every instance configured today lands. The symbol comes from the NAV's asset registry rather than an ERC20 `symbol()` read, because the mainnet underlying is the ETH sentinel `0xEeee…EEeE`, which has no code; an unregistered underlying degrades to the bare address.

  Tier 2 is a coordinate rendering, not a name — the non-ERC20 vault variants expose no human-readable identity on-chain at all (`name()`, `symbol()`, `contractURI()` and `metadataIpfsHash()` all revert; `vaultId()` is a *type* hash that collides across vaults), and the names StakeWise's own UI shows come from off-chain IPFS metadata. Treat the leaf as descriptive, never as an identifier: operator names collide — three separate Gnosis vaults are all named `"SEED"`. Key on `positionId`.

The reconciliation key is `keccak256(abi.encode(chainId, protocolSubId, positionId, balanceAsset.asset, positionKind))` — the `Staking` and `Borrowed` legs reconcile independently via `positionKind`.

***

## Constructor

```solidity
constructor(address underlyingToken_, address osTokenVaultController_, address navCalculator_)
```

| Parameter                 | Description                                                                                                                                                                                      |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `underlyingToken_`        | Underlying staking asset all configured vaults are denominated in (chain-level constant; e.g. ETH sentinel on mainnet, GNO on Gnosis). Must be non-zero                                          |
| `osTokenVaultController_` | StakeWise `OsTokenVaultController` for this chain — converts osToken-debt shares to the underlying for the `Borrowed` leg (the same rate object the osToken price feed uses). Must be a contract |
| `navCalculator_`          | NAVCalculator address, used for the registered-asset registry. Must be a contract                                                                                                                |

***

## Registration

Register the adapter with `addMetaBalanceAdapter(adapter, instances)`, then add or remove vaults with `addMetaInstances(adapter, instances)` / `removeMetaInstances(adapter, instances)`. The instance coordinate is the vault address widened to `bytes32`.


---

# 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/stakewise-v3.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.
