diff --git a/CLAUDE.md b/CLAUDE.md index 3492a07b..d3c609f4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,15 +1,95 @@ -## File Structure +# CLAUDE.md -- `operator/` — current Operator docs (v4.x) -- `operator-v3/` — legacy Operator docs (v3.x) +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -## Code review conventions +## Overview -Every time you perform a code review, make sure that the files with the .md and .mdx extensions in the docs/sdk folder include a title and a description. +The StakeWise documentation site (https://docs.stakewise.io), built with Docusaurus 3.10 + React 19 + TypeScript. Content is MDX; the repo is content-heavy, not application code. +## Commands -## Git Conventions +```bash +yarn # install (Node >= 22, yarn classic) +yarn start # dev server with hot reload +yarn build # production build into build/ — this is what catches broken anchors/links +yarn serve # serve the built site +yarn typecheck # tsc +yarn clear # clear the .docusaurus cache (fixes most stale-build weirdness) +yarn checkRedirects # validate redirects.ts against renamed/deleted content (see below) +``` -Use short commit messages only (no description or co-author). +There is no test suite and no linter. `yarn build` is the real gate: `onBrokenAnchors`, `onDuplicateRoutes`, and `onBrokenMarkdownLinks` are all set to `throw`, so a bad anchor or duplicate route fails the build (plain broken links only warn). -Leave the pull request summary empty when opening a PR, as GitHub Copilot will automatically generate the summary. +## Content architecture + +Three separate docs plugin instances, each with its own content root, route, and sidebar file: + +| Content dir | URL | Sidebar file | Plugin | +|---|---|---|---| +| `docs/` | `/` | `sidebars.ts` | classic preset (`routeBasePath: '/'`) | +| `operator/` | `/operator/*` | `sidebarsOperator.ts` | `plugin-content-docs` id `operator` | +| `staker/` | `/staker/*` | `sidebarsStaker.ts` | `plugin-content-docs` id `staker` | + +Because the default instance serves from `/`, the `docs/` directory holds three navbar sections at once: + +- `docs/docs/**` → `/docs/*` — protocol concepts (vaults, osToken, oracles, fees, governance). Sidebar entries are listed manually in `sidebars.ts` (`docsSidebar`). +- `docs/contracts/**` → `/contracts/*` — autogenerated sidebar. +- `docs/sdk/**` → `/sdk/*` — autogenerated sidebar. + +Adding a page to a manually-listed sidebar (`docsSidebar`, `operatorSidebar`, `stakerSidebar`) requires editing the corresponding `sidebars*.ts`. In autogenerated sections, ordering comes from `_category_.json` (`position`, `label`, optional `link`) plus numeric directory prefixes (`01-vault`, `02-boost`) and `sidebar_position` frontmatter. + +### Synced / mirrored content — do not hand-edit + +- `docs/sdk/**` is synced from the [stakewise/v3-sdk](https://github.com/stakewise/v3-sdk) repo (`external/sdk` is a gitignored submodule; the periodic "Sync SDK documentation" PRs overwrite this tree). Fix SDK docs upstream in v3-sdk, not here. +- `docs/contracts/api/**` mirrors the v3-core Solidity source tree and NatSpec — each page links back to a pinned v3-core commit. Directory layout follows `contracts/` in v3-core. + +### Redirects are mandatory + +Moving, renaming, or deleting any file under `docs/`, `operator/`, or `staker/` requires an entry in `redirects.ts`. This is enforced twice: the husky `pre-commit` hook runs `yarn checkRedirects`, and `.github/workflows/check-redirects.yml` pipes a rename-aware `git diff` into `yarn checkRedirects --stdin`. + +`scripts/checkRedirects/` also rejects duplicate `from` values and verifies that any `#anchor` in a `to` target actually exists as a heading in the destination file. URL derivation (`util/contentRoots.ts`): strip the extension, lowercase, drop a trailing `/index`, and map `docs/` → ``, `operator/` → `/operator`, `staker/` → `/staker`. + +## MDX conventions + +Frontmatter is `title` + `description` (the description feeds SEO metadata and the JSON-LD schema injected by `src/theme/Root.tsx`). Synced SDK pages additionally use `id` and explicit `slug`. + +**Admonitions** — the site uses five custom types instead of the Docusaurus defaults: + +```mdx +:::custom-info[Example] +... +::: +``` + +`custom-info`, `custom-notes`, `custom-tips`, `custom-warning`, `custom-stakewise`. They're rendered by `src/theme/Admonition/Types.js` and must be declared in the `admonitions.keywords` list of **every** docs plugin in `docusaurus.config.ts` — adding a new type means editing three places. + +**Components** — import from the barrel: + +```mdx +import { CommandSnippet, CheckItem, Tooltip } from '@site/src/components'; +``` + +- `CommandSnippet` — code block with inline editable fields; wrap the user-supplied parts in double braces: ````. Multi-line templates use escaped `\\` line continuations. +- `Tooltip` — inline glossary hovers, `content` takes JSX. +- `CheckItem` — checkbox persisted to `localStorage` under `check_{id}`. + +**Images** — `import Image from '@theme/IdealImage'` then `foo`. The theme override (`src/theme/IdealImage/index.tsx`) adds a `sources={{light, dark}}` prop for theme-aware screenshots. + +**Links** — external links get a `↗` suffix, internal cross-references a `→`: `[Automated Node Setup →](/operator/manage-validators/automated-node-setup)`, `[app.stakewise.io ↗](https://app.stakewise.io)`. `src/theme/CustomLink/CustomLink.tsx` handles `target`/`rel` automatically, so never write raw ``. + +## Operator docs are version-split + +`operator/*` documents Operator Service **v4** (current). `operator/operator-service-v3/**` is a parallel, frozen tree for vaults below version 5 on Ethereum / 3 on Gnosis. Both trees have near-identical `alternative-key-management/` pages — when updating one, check whether the change also applies to the other. + +Docker image tag bumps (`europe-west4-docker.pkg.dev/stakewiselabs/public/v3-operator:vX.Y.Z`) live in `operator/launch-operator-service.mdx` and `operator/start-operator.mdx`. + +## Styling + +- SCSS partials in `src/css/*.scss`, all imported by `src/css/custom.scss` (the single `customCss` entry). These target Docusaurus theme classes globally. +- Tailwind v4 is wired through a tiny local plugin (`src/plugins/tailwind-config.js` → `@tailwindcss/postcss`) and is only pulled in by the homepage (`src/pages/index.tsx` imports `src/css/tailwind/config.css`). Doc pages are SCSS-only. +- Components use CSS modules (`*.module.scss`). +- `Root.tsx` sets `data-page="home|inner"` and `data-scrolled` on ``; several styles key off those attributes. + +## Code style (src/) + +No semicolons, single quotes, 2-space indent, `export default X` at the bottom after two blank lines, props typed via a local `type XProps = {...}`. diff --git a/docs/docs/glossary.mdx b/docs/docs/glossary.mdx index 534a5d9b..3249181a 100644 --- a/docs/docs/glossary.mdx +++ b/docs/docs/glossary.mdx @@ -21,7 +21,7 @@ A validator type that supports variable effective balances from 32 ETH up to 204 The process of merging multiple legacy validators (0x01) into compound validators (0x02), or combining the balance of existing compound validators to improve capital efficiency and reduce infrastructure overhead. ### DAO-Approved Vault DAO-Approved Vault -Vaults that meet stringent DAO criteria (defined in [SWIP-24 ↗](https://forum.stakewise.io/t/swip-24-establish-a-policy-for-enabling-100-oseth-minting-in-select-vaults/1724)) and qualify for 100% minting threshold (LTV) for osETH/osGNO—enabling automatic liquid staking token issuance without overcollateralization. +Vaults that meet stringent DAO criteria (defined in [SWIP-24 ↗](https://forum.stakewise.io/t/swip-24-establish-a-policy-for-enabling-100-oseth-minting-in-select-vaults/1724)) and qualify for a near-100% minting threshold (LTV) for osETH/osGNO—enabling automatic liquid staking token issuance without overcollateralization. This maximizes capital efficiency and unlocks advanced use cases like consumer liquid staking, exchange integrations, and leveraged DeFi strategies. Requirements include substantial stake (≥10k ETH / ≥5k GNO), fee caps (≤5% / ≤15%), above-median performance, Vault v3, significant SWISE bond (5M / 1M), and DAO governance approval. diff --git a/docs/docs/ostoken/how-ostoken-works.mdx b/docs/docs/ostoken/how-ostoken-works.mdx index b4ed2a51..7c2c6bbe 100644 --- a/docs/docs/ostoken/how-ostoken-works.mdx +++ b/docs/docs/ostoken/how-ostoken-works.mdx @@ -1,239 +1,207 @@ --- title: How osToken Works -description: Learn about osToken minting, burning, exchange rate, and peg maintenance +description: Learn how osToken works. --- import Image from '@theme/IdealImage' # How osToken Works -osToken turns staked ETH or GNO into liquid, yield-bearing assets. Its operation is governed by four core mechanisms: -minting, burning, exchange rate appreciation, and peg maintenance. +osToken is created through **minting** — issuing new osToken against ETH or GNO staked as collateral in a Vault. A maximum **Loan-to-Value (LTV)** ratio caps how much can be minted against a given stake, keeping every osToken overcollateralized. -## Minting +As the staked collateral accrues rewards, each osToken is worth more ETH or GNO over time. This appreciation is tracked by the osToken **exchange rate** — the protocol-calculated conversion rate between osToken and the underlying asset. This rate is osToken's **fair value**: what one osToken is genuinely worth at any moment. -Minting involves staking assets into a Vault and issuing osTokens against those staked assets acting as collateral. Minting limits are in place to ensure each issued token is fully backed. These limits are set by the **Loan-to-Value (LTV) ratio**. +On the open market, osToken's price can drift from this fair value, but the protocol keeps it **soft-pegged** to the exchange rate from both sides: -### Loan-to-Value Limits +- When osToken trades at a **premium**, anyone can stake, mint at the protocol rate, and sell, pushing the price back down. +- When it trades at a **discount**, **redemptions** convert osToken back to ETH or GNO at the exchange rate, pushing the price back up. -The LTV ratio determines what portion of your stake can be tokenized with osToken: +The protocol keeps every position collateralized: if a rising exchange rate pushes its LTV too high, **liquidations** restore healthy collateralization as a last resort. -| Vault Type | Ethereum (osETH) | Gnosis (osGNO) | Safety Mechanism | -|----------------|------------------|----------------|----------------------------------| -| Standard | 90% LTV | 90% LTV | 10% overcollateralization buffer | -| DAO-Approved | 99.99% LTV | 99.95% LTV | 5M SWISE operator bond | +Together, these mechanisms keep osToken safe to hold and use. -This ensures that even if some validators get slashed, there are enough assets to back all outstanding osTokens. +## How to Get osToken -### LTV Threshold Settings +There are three ways to get osToken: -The StakeWise DAO sets **Loan-to-Value (LTV) thresholds** that determine how much osToken can be minted against staked assets in each Vault. +- [Stake on the main app page](/staker/simple-staking) — the ETH is routed through a [MetaVault](/docs/vaults/meta-vaults) that distributes deposits across the best-performing Sub-vaults (currently just the [Genesis Vault ↗](https://app.stakewise.io/vault/mainnet/0xac0f906e433d58fa868f936e8a43230473652885)), and osToken is minted against the stake automatically. +- [Pick a specific Vault from the marketplace](/staker/vault-staking) — stake into it, then optionally mint osToken against the stake. +- Buy osToken on a DEX. -**Standard Vaults** -- **Default LTV**: 90% of staked ETH/GNO value -- All Vaults can mint osToken up to this threshold by default +## LTV Ratio -**DAO-approved Vaults** -- **Enhanced LTV**: Up to 99.99% of staked value -- Reserved for DAO-approved Vaults meeting strict performance criteria - -:::custom-notes[DAO-approved Vault Requirements] -To qualify for the 99.99% LTV threshold, Vaults must meet all criteria: - -- **Scale**: Minimum 10,000 ETH staked -- **Fee Cap**: Maximum 5% Vault fee -- **Performance**: Consistently above median network performance -- **Technology**: Latest Vault version deployed -- **Collateral**: 5M SWISE tokens locked as slashing insurance -::: - -StakeWise DAO has the capability to increase or decrease the osETH minting threshold based on any given Vault's risk parameters. +The LTV ratio creates a buffer between the collateral and the issued osToken. :::custom-notes[Example] - With 100 ETH staked in a 90% LTV Vault at 1.05 exchange rate: +With 100 ETH staked in a 90% LTV Vault at 1.05 exchange rate: - - Maximum mintable osETH: `100 × 0.9 ÷ 1.05 = 85.71 osETH` - - Overcollateralization: `10 ETH` remains as safety buffer +- Maximum mintable osToken: `100 × 0.9 ÷ 1.05 = 85.71 osToken` +- Overcollateralization: `10 ETH` remains as safety buffer ::: -### Minting & Buying Options - -osToken can be acquired via four methods: +LTV limits vary by Vault type: -1. **Simple staking** – Deposit ETH or GNO through the [StakeWise App ↗](https://app.stakewise.io/) to receive osETH or osGNO. No validator setup or Vault selection required. -2. **Vault staking** – Select a specific Vault (operator, fees, performance). Rewards and penalties are isolated per Vault. Minting capacity is constrained by LTV limits. -3. **Solo staking** – Operate private validators and mint osTokens against self-staked collateral, retaining full infrastructure control. Minting capacity is constrained by LTV limits. -4. **DEX purchase** – Acquire osTokens on decentralized exchanges. Tokens have the same exchange-rate appreciation as minted tokens and do not create a Vault position (no LTV or liquidation exposure). +| Vault Type | Ethereum (osETH) | Gnosis (osGNO) | Safety Mechanism | +|----------------|------------------|----------------|-----------------------------------------------| +| Standard | 90% LTV | 90% LTV | 10% overcollateralization buffer | +| DAO-approved | 99.99% LTV | 99.95% LTV | 5M SWISE operator bond | -All methods yield the same asset that appreciates through the same exchange rate mechanism. +To qualify for the 99.99% LTV, Vaults must meet strict performance criteria1 and post a SWISE operator bond. +StakeWise DAO can adjust any individual Vault's LTV based on its risk profile. -:::custom-notes[Deep Dive] -Find step-by-step instructions for each staking option in the [Staker Guide →](/staker/introduction). -::: +The protocol continuously tracks each position's LTV against the liquidation threshold and puts it into one of four grades — Healthy, Moderate, Risky, and Unhealthy. A **Risky** position is subject to [redemption](#redemption); an **Unhealthy** one to [liquidation](#liquidation). These grades don't apply in **DAO-approved Vaults**. -### Position Health +osToken position health grades - Healthy, Moderate, Risky, Unhealthy -Position health is continuously tracked through LTV thresholds: +## Exchange Rate -osToken position health grades - Healthy, Moderate, Risky, Unhealthy +The osToken **exchange rate** is the protocol-calculated conversion rate between osToken and its underlying staked asset (ETH or GNO). -Positions that exceed **91.5% LTV** become eligible for [redemption](#redemption-mechanism), and those above **92% LTV** are subject to [liquidation](#liquidation-mechanism). +The [`OsTokenVaultController` ↗](https://etherscan.io/address/0x2A261e60FB14586B474C208b1B7AC6D0f5000306) contract keeps two running totals: the total osToken supply (`totalShares`) and the total assets backing it (`totalAssets`). The exchange rate is the ratio between them: -Users can maintain healthy positions by keeping LTV well below limits, adding more collateral, or burning osToken. -In 99.99% (osETH) / 99.95% (osGNO) Vaults, redemptions and liquidations do not apply, positions remain healthy by design. +``` +exchangeRate = totalAssets ÷ totalShares +``` -## Burning +This is what the contract computes whenever it converts between osToken and the underlying asset (`convertToAssets`), so one osToken is always worth `totalAssets ÷ totalShares` of ETH or GNO. -Burning is the process of returning minted osTokens to the Vault in exchange for unlocking the staked collateral that backs them. -To fully unstake, a user must burn the entire amount of osTokens they minted, plus any protocol fees accrued. +The rate rises as staking rewards accrue. On each state update, the protocol adds the newly accrued profit to `totalAssets`, at a pace set by the `avgRewardPerSecond` variable: -Partial burns are also possible, allowing users to reduce debt, improve position health, or free up a portion of their stake. -The burn amount is calculated using the current fair exchange rate to ensure that the redeemed assets always match the true underlying value of the returned tokens. +``` +profit = avgRewardPerSecond × totalAssets × timeElapsed +``` -The amount of ETH that can be unstaked at any moment is determined by maintaining the osToken position within its healthy Loan-to-Value (LTV) range. -After unstaking, the value of minted osETH must remain at or below: -- **90%** of staked ETH in a **90% LTV Vault** -- **99.99%** of staked ETH in a **99.99% LTV Vault** +Because the osToken supply (`totalShares`) doesn't grow with it, the same number of tokens is backed by more assets, so each osToken is worth more. A portion of this profit (`feePercent`) is minted as new osToken to the DAO treasury; the remainder grows the exchange rate for all holders. -The protocol calculates the **maximum unstakable ETH** as: +Only the [`Keeper` ↗](https://etherscan.io/address/0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5) contract — the rewards aggregator for StakeWise's decentralized oracle network — can update `avgRewardPerSecond`. Off-chain, oracles identify the most exposed user (highest LTV across DAO-approved Vaults) and match `avgRewardPerSecond` to that user's Vault APY. -:::custom-notes[Formula - 90% LTV] -$$ -{\small MaxUnstakeableETH = StakedETH - \dfrac{osETHMinted \times ExchangeRate}{0.9}} -$$ -::: +This guarantees osToken appreciation never outpaces the collateral backing it: even the most exposed position grows fast enough to stay solvent. -:::custom-notes[Formula – 99.99% LTV] -$$ -{\small MaxUnstakeableETH = StakedETH - \dfrac{osETHMinted \times ExchangeRate}{0.9999}} -$$ -::: +## Redemption -:::custom-notes[Example: Bob's 90% LTV Position] -Bob stakes **100 ETH** and mints **50 osETH** at an exchange rate of **1.05 ETH/osETH**. -- Value of minted osETH: `50 × 1.05 = 52.5 ETH` -- Position health: `52.5 ÷ 100 = 52.5%` → safely below **90%** -- Maximum unstakable ETH: `100 − (52.5 ÷ 0.9) = 41.667 ETH` +Redemption is the process of converting osToken back to its underlying asset (ETH or GNO) at the protocol exchange rate. -After unstaking **41.667 ETH**, Bob's position health = **90%** -::: +Redemptions run through the [`OsTokenRedeemer` ↗](https://etherscan.io/address/0xc43A7b16A7a167c0318390Cba16787C11e9e1FD0) contract. Vaults version 1 (mainnet only) retain a separate position-level mechanism that's currently disabled. -:::custom-notes[Alice's 99.99% LTV Position] -Alice stakes **100 ETH** and mints **50 osETH** at the same **1.05** rate. -- Value of minted osETH: `52.5 ETH` -- Position health: `52.5 ÷ 100 = 52.5%` → well below **99.99%** -- Maximum unstakable ETH: `100 − (52.5 ÷ 0.9999) = 47.495 ETH` +### Redeemer Redemption -After unstaking **47.495 ETH**, Alice's position health = **99.99%** -::: +osToken's market price on DEXes can drift from the protocol exchange rate. Anyone holding osToken can redeem it at the protocol exchange rate by entering the `OsTokenRedeemer`'s exit queue — no whitelist, no privileged role — which is what makes a discount worth arbitraging away. Queue entry is open to all; only the on-chain execution against minters' positions is carried out by the Operator Service. -## Peg Maintenance +See [osToken Redemptions](/docs/ostoken/ostoken-redemptions) for the exact mechanics. -osToken maintains a soft peg to its underlying asset (ETH or GNO) through minting, redemptions, and liquidations. -Minting helps prevent secondary market premiums on osTokens. -Redemption acts as early intervention when positions drift toward risky erosion of collateral, -while liquidation serves as the last resort for closing thinly collateralized positions. -Together, they create arbitrage opportunities that keep osToken's market price closely aligned with its fair exchange rate. +### Position-Level Redemption -### Redemption Mechanism +This mechanism exists only on Version 1 Vaults (mainnet) and is currently disabled. The description below covers how it works when active. -In **90% LTV Vaults**, the redemption mechanism activates when a position's Loan-to-Value (LTV) reaches the **redemption threshold of 91.5%**. -At this point, anyone can burn part of that position's osTokens in exchange for an equivalent share of the Vault's collateral. -The burn amount is calculated so that the position's LTV is restored precisely to 90%. -Importantly, the staker whose tokens are redeemed does not lose value – they keep the osTokens burned on their behalf. +When a position's LTV crosses 91.5%, anyone can redeem part of its osToken against its collateral to restore the LTV to 90%. :::custom-notes[Formula: Redeemable Amount (90% LTV)] $$ -{\small Redeemable\ osETH = \frac{10 \times Minted\ osETH \times Exchange\ Rate - 9 \times Staked\ ETH}{Exchange\ Rate}} +{\small Redeemable\ osToken = \frac{Minted\ osToken \times Exchange\ Rate - 0.9 \times Staked\ ETH}{0.1 \times Exchange\ Rate}} $$ + +Derived by solving `(Minted − R) × Rate / (Staked − R × Rate) = 0.9`, where `R` is the redeemable osToken amount. ::: -:::custom-notes[Example: Bob's Redemption at 91.6% LTV] -Bob stakes **100 ETH** and mints **87.238 osETH** at an exchange rate of **1.05 ETH/osETH**. +:::custom-notes[Example: Redemption Above the 91.5% Threshold] +Bob stakes **100 ETH** and mints **85.714 osToken** at an exchange rate of **1.05 ETH/osToken** — the 90% LTV cap. -- Value of minted osETH: `87.238 × 1.05 = 91.6 ETH` -- Loan-to-Value: `91.6 ÷ 100 = 91.6%` → above **91.5% threshold** +Over time, the exchange rate rises to **1.0687**, lifting his LTV: +- Value of minted osToken: `85.714 × 1.0687 ≈ 91.6 ETH` +- Loan-to-Value: `91.6 ETH ÷ 100 ETH = 91.6%` → above **91.5% threshold** **Redemption calculation:** -- Redeemable osETH = `(10 × 87.238 × 1.05 - 9 × 100) ÷ 1.05 = 15.238` -- Redeemer burns **15.238 osETH** and receives **16 ETH** +- Redeemable osToken = `(85.714 × 1.0687 − 0.9 × 100) ÷ (0.1 × 1.0687) = 1.6 ÷ 0.10687 ≈ 14.97` +- The redeemer burns **14.97 osToken** and receives **16 ETH** from Bob's collateral (`14.97 × 1.0687`) **After redemption:** -- Bob's minted balance: **72 osETH** +- Bob's minted balance: **70.74 osToken** - Bob's staked collateral: **84 ETH** -- New LTV: **90%** (restored to safety) -- Bob **retains the 15.238 osETH** burned on his behalf → **no value lost** +- New LTV: `(70.74 × 1.0687) ÷ 84 ETH = 90%` (restored to the cap) +- The position shrinks at fair value: the released ETH matches the debt cleared at the protocol exchange rate, with no value lost. ::: -Positions in **99.99% LTV Vaults** are also subject to redemption, with amounts redeemed determined by the protocol's redemption request volume. -The LTV of redeemed positions will remain above or equal to 99.99% after redemption, not affecting the position owner in any scenario. +## Liquidation -### Liquidation Mechanism +Liquidation is the last resort for closing thinly collateralized positions. In **90% LTV Vaults**, the liquidation threshold is set to **92% LTV**: when a position crosses it, anyone can liquidate part or all of it. -Liquidation is the system's final safeguard, closing unhealthy positions to protect overcollateralization. -In **90% LTV Vaults**, if a position exceeds the **92% LTV liquidation threshold**, anyone can close it entirely by burning all minted osTokens against the collateral. -The liquidator receives the underlying collateral value plus a **1% liquidation premium**, deducted from the staker's collateral as a penalty. +A liquidator burns their own osToken to clear a portion of the staker's debt and receives the underlying value plus a **1% premium**, deducted from the staker's collateral. :::custom-notes[Formula: Liquidation Payout] $$ -{\small ETH\ Payout = (Minted\ osETH \times Exchange\ Rate) \times 1.01} +{\small ETH\ Payout = (Burned\ osToken \times Exchange\ Rate) \times 1.01} $$ ::: -:::custom-notes[Example: Alice's Liquidation at 92% LTV] -Alice stakes **100 ETH** and mints **87.629 osETH** at an exchange rate of **1.05 ETH/osETH**. +:::custom-notes[Example: Full Liquidation at 92.01% LTV] +Alice stakes **100 ETH** and mints **85.714 osToken** at an exchange rate of **1.05 ETH/osToken** — the 90% LTV cap. -- Value of minted osETH: `87.629 × 1.05 = 92.01 ETH` -- Loan-to-Value: `92.01 ÷ 100 = 92.01%` → above **92% threshold** +Over time, the exchange rate rises to **1.0735**, lifting her LTV past the **92% liquidation threshold**: +- Value of minted osToken: `85.714 × 1.0735 ≈ 92.01 ETH` +- Loan-to-Value: `92.01 ETH ÷ 100 ETH = 92.01%` → above the **92% threshold** -**Liquidation process:** -- ETH payout = `(87.629 × 1.05) × 1.01 = 92.93 ETH` -- Liquidator burns **87.629 osETH** and receives **92.93 ETH** +**Liquidation (full):** +- A liquidator burns **85.714 osToken** from their wallet +- ETH payout = `(85.714 × 1.0735) × 1.01 ≈ 92.93 ETH`, taken from Alice's collateral -**After liquidation:** -- Alice's minted balance: **0 osETH** +**After:** +- Alice's minted balance: **0 osToken** (debt cleared) - Alice's remaining collateral: **7.07 ETH** -- Alice keeps her original **87.629 osETH** -- **Loss: 1% of collateral** as liquidation penalty +- Alice's wallet osToken: unchanged +- Net loss: **~0.92 ETH** — the 1% premium on the liquidated debt, deducted from her collateral ::: -Positions in **99.99% LTV Vaults** are exempt from liquidation, as their APY parity ensures position health remains stable. +Positions in **99.99% LTV Vaults** are exempt: liquidations are disabled at the contract level. The exemption is safe because the exchange rate appreciation is capped at those Vaults' APY, so positions stay solvent by design. + +## Burning -## Exchange Rate Appreciation +Burning is the process of returning minted osToken to the Vault in exchange for unlocking the staked collateral that backs it. -osToken is a **repricing token**, meaning its redemption value increases over time as staking rewards accrue. -Each osToken represents a growing share of the underlying staked ETH or GNO. +Users can burn any amount: a full burn clears the entire debt, while partial burns reduce debt, improve LTV, and free up a portion of the stake. Protocol fees accrue automatically into the position over time, so a full burn includes the original mint plus accrued fees. The burn amount is calculated at the current protocol exchange rate. -The **fair exchange rate** is the amount of assets a user can receive when redeeming osToken within the protocol. -As rewards accrue, the redemption value increases, creating the "repricing" effect where each osETH becomes worth more ETH over time. +The full stake can only be withdrawn once the osToken position is fully burned. While any osToken remains minted, the Vault keeps enough ETH locked to back it, so only part of the stake can be unstaked. -The fair exchange rate is reflected through `convertToAssets` and `convertToShares` functions in the [osTokenVaultController ↗](https://etherscan.io/address/0x2A261e60FB14586B474C208b1B7AC6D0f5000306#code) contract, -which manages osToken supply, collateral, and reward accrual. +The amount of ETH that can be unstaked at any moment is determined by maintaining the osToken position within its LTV cap. After unstaking, the value of minted osToken must remain at or below: -## Reward Accrual & Fee Mechanism +- **90%** of staked ETH in a **90% LTV Vault** +- **99.99%** of staked ETH in a **99.99% LTV Vault** + +The protocol calculates the **maximum unstakable ETH** as: -A decentralized Oracle network uses the [VaultUserLtvTracker ↗](https://etherscan.io/address/0xe0ae8b04922d6e3fa06c2496a94ef2875efcc7bb#code) contract to identify the user with the highest osToken LTV position across all Vaults. -Oracles then set the `avgRewardPerSecond` parameter to match that user's Vault APY performance. -This rate drives the continuous calculation of osToken value appreciation: +:::custom-notes[Formula: 90% LTV] +$$ +{\small MaxUnstakableETH = StakedETH - \dfrac{osTokenMinted \times ExchangeRate}{0.9}} +$$ +::: -:::custom-notes[Formula: Profit Accrual] +:::custom-notes[Formula: 99.99% LTV] $$ -{\small profitAccrued = avgRewardPerSecond \times totalAssets \times timeElapsed} +{\small MaxUnstakableETH = StakedETH - \dfrac{osTokenMinted \times ExchangeRate}{0.9999}} $$ ::: -This mechanism ensures that osETH appreciation keeps pace with the least collateralized position in the entire protocol. -When necessary, Oracles adjust `avgRewardPerSecond` downward to prevent any LTV from exceeding 100% due to insufficient Vault performance. +:::custom-notes[Example: Bob's 90% LTV Position] +Bob stakes **100 ETH** and mints **50 osToken** at an exchange rate of **1.05 ETH/osToken**. -From these rewards, the protocol deducts a 5% fee that increases users' osETH debt—the amount they must burn to fully exit their positions. -This fee creates new tokens for the StakeWise DAO Treasury and increases the osETH balance that users owe back to their Vaults. -The remaining 95% of rewards increase the backing assets, raising the fair exchange rate for all osToken holders. +- Value of minted osToken: `50 × 1.05 = 52.5 ETH` +- LTV: `52.5 ÷ 100 = 52.5%` → safely below **90%** +- Maximum unstakable ETH: `100 − (52.5 ÷ 0.9) = 41.667 ETH` -:::custom-notes[Example: Fee Impact] -If you minted **1 osToken** and after fee collection it increases to **1.000001**, -your debt grows by **0.000001**—your proportional share of the protocol's accumulated fees. +After unstaking **41.667 ETH**, Bob's LTV = **90%**. ::: -:::custom-info[Learn More] -For comprehensive information about all protocol fees, see the [Fees →](../fees/intro) chapter. +:::custom-notes[Example: Alice's 99.99% LTV Position] +Alice stakes **100 ETH** and mints **50 osToken** at the same **1.05** rate. + +- Value of minted osToken: `52.5 ETH` +- LTV: `52.5 ÷ 100 = 52.5%` → well below **99.99%** +- Maximum unstakable ETH: `100 − (52.5 ÷ 0.9999) = 47.495 ETH` + +After unstaking **47.495 ETH**, Alice's LTV = **99.99%**. ::: + +When a user exits a Vault while still holding osToken debt, the Vault may not have liquid ETH immediately available, since validators must go through the Ethereum exit queue. An escrow contract holds the osToken position alongside the underlying stake until ETH is freed, then unwinds both in lockstep. This keeps every osToken fully backed throughout the exit process. + +
+ 1. DAO-approved Vault requirements: minimum 10,000 ETH staked, maximum 5% Vault fee, consistently above-median network performance, latest Vault version deployed, and 5M SWISE tokens locked as slashing insurance. ↩ +
diff --git a/docs/docs/ostoken/img/grade.png b/docs/docs/ostoken/img/grade.png index d1bc8471..63531e97 100644 Binary files a/docs/docs/ostoken/img/grade.png and b/docs/docs/ostoken/img/grade.png differ diff --git a/docs/docs/ostoken/img/redemptions.png b/docs/docs/ostoken/img/redemptions.png new file mode 100644 index 00000000..58c09460 Binary files /dev/null and b/docs/docs/ostoken/img/redemptions.png differ diff --git a/docs/docs/ostoken/intro.mdx b/docs/docs/ostoken/intro.mdx index e3b5c901..5c8d93e2 100644 --- a/docs/docs/ostoken/intro.mdx +++ b/docs/docs/ostoken/intro.mdx @@ -1,21 +1,19 @@ --- title: osToken -description: Learn what osToken is and how to get it +description: osToken is StakeWise's liquid, overcollateralized staking token on Ethereum and Gnosis. --- import Image from '@theme/IdealImage' # osToken -osToken is a liquid staking token that accrues staking rewards when held. It is a generic name for StakeWise's network-specific ERC-20 tokens: +ETH and GNO staked in a Vault earn rewards, but the stake itself is a non-transferable share of the Vault's staking pool. osToken turns that stake into a transferable asset that can be [used across DeFi ↗](https://app.stakewise.io/ecosystem) to earn additional rewards. + +osToken is the generic name for StakeWise's network-specific liquid staking ERC-20 tokens: - **osETH** on Ethereum - **osGNO** on Gnosis Chain -osToken overview - liquid staking token for StakeWise - -osToken is issued against assets staked in a Vault to allow using staked capital in decentralized applications to trade, borrow, lend, and restake. Hence, osTokens represent the underlying staked assets and the rewards earned. +osToken is overcollateralized: more ETH or GNO is always staked than the osToken issued against it. As a repricing token, osToken accrues staking rewards by appreciating against the underlying asset, rather than increasing the token balance in the user's wallet. -Short for **Overcollateralized Staked Token**, osToken requires that the value of staked assets exceed the value of osToken issued. This overcollateralization acts as a safety buffer, protecting holders and the protocol from validator penalties or underperformance of permissionless Vaults. - -A defining feature is that osToken can be minted against validators run by anyone – from solo stakers to professional operators – making access to liquid staking fully permissionless and non-custodial. +osToken overview - liquid staking token for StakeWise diff --git a/docs/docs/ostoken/ostoken-redemptions.mdx b/docs/docs/ostoken/ostoken-redemptions.mdx new file mode 100644 index 00000000..4f576dc1 --- /dev/null +++ b/docs/docs/ostoken/ostoken-redemptions.mdx @@ -0,0 +1,101 @@ +--- +title: osToken Redemptions +description: How osToken redemptions work under the hood. +--- + +import Image from '@theme/IdealImage'; + +# osToken Redemptions + +Redemptions are an in-protocol peg-maintenance mechanism that converts osToken back to ETH or GNO at the protocol [exchange rate](/docs/ostoken/how-ostoken-works#exchange-rate). + +Every osToken is minted against Vault collateral (the user's stake in the Vault) and must remain backed by that collateral. The minter takes on a corresponding debt against their Vault collateral and can burn the osToken at any time to repay the debt and withdraw their stake. + +If the minter transfers the osToken away, the minter's on-chain debt in the Vault is unchanged. From the protocol's perspective: the minter owes `X` osToken worth of debt, but only `Y < X` is still in their hands. The delta `X − Y` is what the protocol calls **redeemable** — osToken that exists somewhere but is no longer tied to the minter. + +Redemption burns the missing portion of the minter's debt against their collateral and releases the corresponding ETH/GNO at the protocol exchange rate. + +## How Redemptions Work + +osToken redemption flow between the Operator Service and the OsTokenRedeemer contract + +The redemption flow is coordinated between four actors: **osToken holders**, who supply the osToken to redeem by entering the redeemer's exit queue; the [Operator Service ↗](https://github.com/stakewise/v3-operator), which computes who can be redeemed and by how much, and executes the redemptions on-chain as the redeemer's `positionsManager`; **StakeWise**, which authorizes the list of redeemable positions on-chain; and the [OsTokenRedeemer ↗](https://etherscan.io/address/0xc43A7b16A7a167c0318390Cba16787C11e9e1FD0) contract, which verifies and settles them. + +The flow draws on two independent tracks that converge once both are ready: the Operator Service publishes the list of redeemable positions (Step 1), while osToken holders queue osToken for redemption (Step 2). The remaining steps consume both. In full, it runs in seven steps: + +### 1. Operator Service Computes the Redeemable Positions + +The Operator Service uploads the list of eligible positions to [IPFS ↗](https://ipfs.stakewise.io/ipfs/bafkreihbwhyaqskcczhuanu2nbdgdlfbwfwgbirue6kvlw4nrc5d2snqxa) and builds a Merkle tree that commits to each entry. StakeWise then submits the root and IPFS hash on-chain to authorize the listed positions for redemption. + +:::custom-notes[Under the Hood] +The Operator Service computes the redeemable positions in five steps: + +1. Pin the snapshot to a finalized block so all the following steps read the same on-chain state. +2. Fetch all allocators (addresses that have minted osToken) from the subgraph. +3. Skip [Boost](/docs/vaults/boost) positions. Each Boost leverage position has its own proxy contract that holds the osToken on the user's behalf, so those proxy addresses are removed from the minters list, and each user's leveraged shares are subtracted from their balance to avoid double-counting. +4. Compute `kept` shares — osToken in trackable locations: wallet balances on the Vault's own chain and DeFi protocols on that chain indexed by DeBank or Rabby. Anything held elsewhere, including on other chains, is treated as missing. +5. Compute `redeemable = minted − kept`, split it across the user's Vaults proportionally to where they minted, and sort by LTV descending then amount descending so the riskiest positions are drawn down first. +::: + +### 2. osToken Enters the Queue + +Anyone holding osToken can enter the redeemer's exit queue — there is no whitelist and no privileged role. `enterExitQueue` is permissionless: the caller approves (or [permits ↗](https://eips.ethereum.org/EIPS/eip-2612)) their osToken to the `OsTokenRedeemer`, and the contract pulls the shares in and issues a ticket — a unique cumulative index recording the entry's place in the queue and how much is owed — carrying the right to claim ETH/GNO later. + +In practice this is done from the main page of [app.stakewise.io ↗](https://app.stakewise.io), which wraps the approval and the queue entry into a single flow. + +This is what maintains the peg. Redemption pays out at the protocol [exchange rate](/docs/ostoken/how-ostoken-works#exchange-rate), so whenever osToken trades below that rate on the secondary market, buying it and redeeming it through the queue is profitable. That arbitrage is open to anyone, and it is what pulls the market price back toward the protocol rate. + +### 3. Operator Service Prepares the Next Redemption + +The Operator Service monitors the on-chain state. If a previous redemption round has redeemed ETH/GNO that is waiting to be checkpointed, it first calls `processExitQueue` to finalize that batch so the assets become claimable. + +It then checks whether there's enough queued osToken to submit a new redemption. + +### 4. Operator Service Submits a Redemption + +The Operator Service downloads the published list from IPFS, picks a batch of eligible positions, and decides how much to redeem from each. If a target Vault is a MetaVault without enough liquidity on hand, the Operator Service first pulls assets up from Sub-vaults via a separate `redeemSubVaultsAssets` transaction. It then refreshes the state of the target Vaults in a separate multicall transaction, builds a Merkle multiproof against the published root, and calls `redeemOsTokenPositions` on `OsTokenRedeemer` for each position. + +The redemption is now in flight; verification and execution happen on-chain. + +### 5. OsTokenRedeemer Executes the Redemption + +The contract rebuilds each leaf, verifies the Merkle multiproof against the stored root, and caps the amount independently per position. For each verified position, it calls the Vault's `redeemOsToken` to burn the minter's osToken debt and send the equivalent ETH/GNO (using the Vault's just-updated state) to the redeemer. + +The queued shares are now matched against missing positions — settled, but not yet claimable. + +### 6. Batch Is Checkpointed + +Once the configured delay has elapsed — 12 hours on both Ethereum and Gnosis — `processExitQueue` creates a new checkpoint that matches the redeemed shares to their ETH/GNO and marks the assets as claimable. This call is also permissionless, so anyone can trigger it; in practice the Operator Service does. Tickets that fall within this checkpoint can now be claimed. + +::::custom-notes[Dive Deeper: The Exit Queue and Checkpoints] + +**The queue** + +:::custom-info[Not the Vault Exit Queue] +This is the `OsTokenRedeemer`'s own exit queue, independent of the Vault exit queue. +::: + +When osToken enters the queue, the assets aren't released right away. The contract tracks this line with a single number, the `positionTicket`: + +`positionTicket = (all previously processed shares) + (shares already queued ahead)` + +The queue clears in one of two ways, both of which move shares out of *queued* and pair them with assets: + +- **Redemption** — the Operator Service redeems the queued shares against the published redeemable positions (Steps 4–5), drawing ETH/GNO out of the Vaults. +- **Swap** — anyone can call `swapAssetsToOsTokenShares` (sending ETH, or approving GNO on Gnosis) to take queued osToken off the front of the queue at the protocol exchange rate. This fills the queue directly, without touching any minter's position. + +Either way the shares are matched with ETH/GNO but can't be claimed yet — that requires a checkpoint. + +**Checkpoints** + +A checkpoint is a snapshot that says: *"at this point in the queue, this many shares were exchanged for this many assets."* When a ticket is claimed, the checkpoint covering it tells the contract: + +- how many tickets it now covers (marked exited), +- and how many assets those tickets are worth, at the rate the checkpoint locked in. + +If only some of the ticket is covered and the rest is still queued, the contract pays out the covered portion and rolls the remainder into a new exit request at the next ticket. The rest can be claimed after the next checkpoint. +:::: + +### 7. ETH/GNO Is Claimed + +The ticket holder calls `claimExitedAssets` with their ticket and the matching checkpoint index, and the contract pays out the corresponding ETH/GNO. If the ticket spans more than one checkpoint, the covered portion is paid out and the remainder rolls into a new ticket that can be claimed after a later checkpoint. diff --git a/docs/docs/stakewise-protocol/what-is-stakewise.mdx b/docs/docs/stakewise-protocol/what-is-stakewise.mdx index 6dfce22e..3f638b2a 100644 --- a/docs/docs/stakewise-protocol/what-is-stakewise.mdx +++ b/docs/docs/stakewise-protocol/what-is-stakewise.mdx @@ -66,7 +66,7 @@ The decentralized Oracle network connects StakeWise smart contracts to Ethereum' - **Validator Lifecycle**: Manages validator registration, consolidations, rewards, penalties, and exits. -- **Token Stability**: Maintains accurate osToken exchange rates and safeguards peg stability. [Learn more →](../ostoken/how-ostoken-works#reward-accrual--fee-mechanism) +- **Token Stability**: Maintains accurate osToken exchange rates and safeguards peg stability. [Learn more →](../ostoken/how-ostoken-works#exchange-rate) :::custom-notes[Deep Dive] Learn more about [Oracles →](../oracles/intro) diff --git a/docs/docs/vaults/how-vaults-work.mdx b/docs/docs/vaults/how-vaults-work.mdx index e5bf0ec9..05464f7d 100644 --- a/docs/docs/vaults/how-vaults-work.mdx +++ b/docs/docs/vaults/how-vaults-work.mdx @@ -118,7 +118,7 @@ Once a validator fully exits, its balance is [swept ↗](https://ethereum.org/en osTokens (`osETH` or `osGNO`) are ERC-20 tokens that users can mint from any Vault using their shares as backing (aka collateral), providing liquidity without unstaking. osToken can be traded or used in DeFi while the underlying stake continues earning rewards. -The system keeps more staked assets backing each osToken than the token is worth. This safety buffer protects users and keeps the system stable. The Vault's Loan-to-Value ratio determines how much users can mint — this ranges from standard ratios around 90% up to 99.99% for DAO-approved Vaults6. +For all Vaults other than DAO-approved Vaults with 99.99% LTV enabled, the system maintains more staked assets backing each osToken than the token is worth. This safety buffer protects users and helps keep the system stable. A Vault's loan-to-value (LTV) ratio determines how much users can mint, ranging from the standard ratio of 90% to 99.99% for DAO-approved Vaults.6 :::custom-notes[Deep Dive] For more details on how osToken works, see [osToken →](../ostoken/intro). diff --git a/sidebars.ts b/sidebars.ts index 0181b796..c28e5e6d 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -42,6 +42,7 @@ const sidebars: SidebarsConfig = { }, items: [ 'docs/ostoken/how-ostoken-works', + 'docs/ostoken/ostoken-redemptions', ], }, { diff --git a/staker/risks.mdx b/staker/risks.mdx index 10922824..36fd5d2e 100644 --- a/staker/risks.mdx +++ b/staker/risks.mdx @@ -19,7 +19,7 @@ All DeFi protocols carry smart contract risk. StakeWise runs on battle-tested, [ ## osETH Depeg osETH Depeg -osETH could temporarily trade below its fair value on secondary markets. The protocol's [redemption and liquidation mechanisms](/docs/ostoken/how-ostoken-works#peg-maintenance) create arbitrage incentives that keep the market price aligned with the underlying value. +osETH could temporarily trade below its fair value on secondary markets. The protocol's [redemption mechanism](/docs/ostoken/ostoken-redemptions) keeps the market price tightly tethered to the protocol exchange rate: anyone can buy discounted osETH and redeem it for ETH at the protocol exchange rate (or mint and sell fresh osETH when it trades at a premium), and that arbitrage pulls the price back. ## Boost Boost diff --git a/staker/vault-staking.mdx b/staker/vault-staking.mdx index 45e5a03d..a0b79807 100644 --- a/staker/vault-staking.mdx +++ b/staker/vault-staking.mdx @@ -28,8 +28,10 @@ Pick a Vault that matches your preferences and start staking in just a few steps Your stake starts earning rewards as soon as the Vault's validators are active. +osETH can only be minted in Vaults that are **collateralized** (have active validators) and **harvested** (rewards synced via the Keeper). + :::custom-info[Position Health] -If you mint osETH, your position gets a [health score](/docs/ostoken/how-ostoken-works#position-health) based on how much osETH you've minted relative to your staked ETH (the Loan-to-Value ratio). There are four health levels — healthy, moderate, risky, and unhealthy. If your position becomes unhealthy, it may be subject to [redemption or liquidation](/docs/ostoken/how-ostoken-works#peg-maintenance). You can improve it anytime by burning osETH or adding more stake. +If you mint osETH, your position gets a [health score](/docs/ostoken/how-ostoken-works#ltv-ratio) based on how much osETH you've minted relative to your staked ETH (the Loan-to-Value ratio). There are four health levels — healthy, moderate, risky, and unhealthy. A risky position is subject to [redemption](/docs/ostoken/how-ostoken-works#redemption), while an unhealthy one is subject to [liquidation](/docs/ostoken/how-ostoken-works#liquidation). You can improve it anytime by burning osETH or adding more stake. Position health depends on the APY of osETH relative to the Vault APY. The larger the gap (Vault APY < osETH APY), the worse the position health can get. If the Vault is running normally with close to the average APY across all Vaults, it's very unlikely that the position will drop below healthy. :::