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

# Convex

The Convex adapter (`ConvexCurveLPStakingMetaBalanceAdapter`) reports an account's Convex-staked Curve LP positions across any configured Convex pool, plus all claimable rewards (CRV, CVX, and extra tokens). A single immutable meta-adapter serves every pool; the active set of Convex rewarders lives on NAVCalculator.

* **Type:** Meta-adapter
* **protocolSubId:** `keccak256("convex-curve-lp-staking")`
* **Instance key:** the Convex `BaseRewardPool` (rewarder) address, widened to bytes32. The underlying Curve LP token and pool are resolved on-chain via `rewarder.pid()` → `Booster.poolInfo(pid).lptoken` → `CurvePoolLib.getPool(lpToken)`.
* **Source:** [`ConvexCurveLPStakingMetaBalanceAdapter.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/balances/MetaBalanceAdapters/ConvexCurveLPStakingMetaBalanceAdapter.sol)

***

## Positions returned

Per rewarder instance:

| Leg                                      | PositionKind | isDebt  | Description                                                                                                                                                          |
| ---------------------------------------- | ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Staked LP coin (one per Curve pool coin) | `Staking`    | `false` | Pro-rata share of each Curve pool coin backing the staked LP                                                                                                         |
| Staked LP token (fallback)               | `Staking`    | `false` | The LP token itself, emitted **instead** of the per-coin legs when the Curve pool cannot be resolved structurally — so the principal is reported rather than dropped |
| CRV reward                               | `Rewards`    | `false` | Claimable CRV (main reward token)                                                                                                                                    |
| CVX reward                               | `Rewards`    | `false` | CVX derived from CRV via the cliff schedule (skipped if `CVX_TOKEN == address(0)`)                                                                                   |
| Extra reward (up to 8)                   | `Rewards`    | `false` | Each enumerated extra reward token; stash wrappers unwrapped to the underlying                                                                                       |

Legs with zero amount, unregistered assets, or assets excluded by `assetFilter` are dropped.

***

## Balance calculation

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

**Staking legs** — `stakedLp = rewarder.balanceOf(account)`. For each Curve pool coin `i`:

```
amount = poolBalance[i] × stakedLp / lpToken.totalSupply()
```

(`CurvePoolLib.proRataAmount`, using `getPoolBalance`, `getTotalSupply`, `getCoins`.)

**CRV reward:**

```
crvEarned = rewarder.earned(account)
```

This is exact and view-safe — Convex accounts for all pending CRV in `earned()`, so it is not a lower bound.

**CVX reward** — derived from `crvEarned` via the Convex cliff emission schedule:

```
cliff      = CVX.totalSupply() / 100_000e18           // reductionPerCliff
remaining  = 1_000 - cliff                            // totalCliffs = 1000
cvxAmount  = crvEarned × remaining / 1_000
cvxAmount  = min(cvxAmount, 100_000_000e18 - supply)  // cap at max supply
```

Returns 0 once `cliff >= 1000`.

**Extra reward legs** — for each extra rewarder up to 8: `amount = extraRewarder.earned(account)`. The reward token is unwrapped from any stash wrapper (`stash.token()` → underlying). Tokens equal to CRV are skipped (already reported); CVX via a stash wrapper is skipped (already derived in the CVX leg).

***

## Identity

* **positionId:** `abi.encode(address rewarder)` — one positionId per rewarder instance (all legs of an instance share it).
* **positionKind:** `Staking` for LP coins, `Rewards` for CRV / CVX / extra tokens.

There is no `labels` field on the position. The adapter implements `positionLabels(positionId)`, which returns the 3-segment breadcrumb `["Staking", "Curve LP", "TOKEN0/TOKEN1/…"]` — the third segment being the underlying Curve pool's coin symbols, derived on-chain from the rewarder's LP token (collapses to `["Staking", "Curve LP"]` if unreadable). It is surfaced only by the verbose reads (`getAccountPositionsVerbose` / `getAccountNavVerbose`); the full breadcrumb is `[protocolName, ...labels]`.

***

## Constructor

```solidity
constructor(
    address booster_,
    address cvxToken_,
    address navCalculator_
)
```

| Parameter        | Description                                                                                                               |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `booster_`       | Convex Booster contract (resolves `lptoken` from a pool ID; mainnet `0xF403C135812408BFbE8713b5A23a04b3D48AAE31`)         |
| `cvxToken_`      | CVX token address (mainnet `0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B`); pass `address(0)` to skip CVX computation on L2 |
| `navCalculator_` | NAVCalculator contract for asset-registry access                                                                          |

***

## Registration

Register the adapter once with `addMetaBalanceAdapter`, then enable each pool with `addMetaInstances`. The instance coordinate is the Convex `BaseRewardPool` (rewarder) address widened to bytes32. The adapter holds no instance state — it reads its active set from `NAV_CALCULATOR.getMetaInstances(address(this))`.


---

# 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/convex.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.
