# Contract Reference

This is the canonical API reference for ETH Strategy smart contracts. Every public and external function is documented with a consistent template:

> **Signature** → **Parameters** → **Returns** → **Access Control** → **Reverts** → **Why It Exists**

For integration patterns, production warnings, and working code examples, see the [Integration Guide](/developers/integration-guide.md). For deployed addresses, see [Contracts](/references/contracts.md).

***

## esETH

Non-rebasing ERC-20 wrapper around multiple liquid staking tokens (wstETH, rETH, cbETH, weETH, aWETH), pegged 1:1 with ETH. The denomination layer for the entire treasury.

{% hint style="info" %}
**Status: Deploying soon.** Address will be published on the [Contracts](/references/contracts.md) page before permissionless launch.
{% endhint %}

### `mint(address lst, uint256 amount, address recipient) → uint256 esEthAmount`

Deposit a supported LST and receive esETH at a 1:1 ratio with the ETH value of the deposit.

| Parameter   | Type      | Description                                                          |
| ----------- | --------- | -------------------------------------------------------------------- |
| `lst`       | `address` | Address of the LST to deposit (wstETH, rETH, cbETH, weETH, or aWETH) |
| `amount`    | `uint256` | Amount of the LST to deposit                                         |
| `recipient` | `address` | Address to receive the minted esETH                                  |

| Returns       | Type      | Description            |
| ------------- | --------- | ---------------------- |
| `esEthAmount` | `uint256` | Amount of esETH minted |

| Access       |                                                                     |
| ------------ | ------------------------------------------------------------------- |
| **Caller**   | Any (permissionless)                                                |
| **Requires** | Caller must have approved esETH contract to spend `amount` of `lst` |

| Reverts       | When                                               |
| ------------- | -------------------------------------------------- |
| —             | `lst` is not a supported token                     |
| —             | `amount` is zero                                   |
| ERC-20 revert | Caller has insufficient `lst` balance or allowance |

**Why:** Primary entry point for adding collateral to the protocol. Wrapping multiple LSTs into a single non-rebasing token pegged 1:1 with ETH simplifies treasury accounting and enables a unified denomination layer across all protocol operations.

***

### `mint(address recipient) → uint256 esEthAmount`

Deposit raw ETH and receive esETH. The sent ETH is staked via the most favorable LST route.

| Parameter   | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `recipient` | `address` | Address to receive the minted esETH |

| Returns       | Type      | Description            |
| ------------- | --------- | ---------------------- |
| `esEthAmount` | `uint256` | Amount of esETH minted |

| Access       |                      |
| ------------ | -------------------- |
| **Caller**   | Any (permissionless) |
| **Requires** | `msg.value > 0`      |

| Reverts | When                |
| ------- | ------------------- |
| —       | `msg.value` is zero |

**Why:** Convenience overload so users don't need to manually acquire an LST before minting. The contract routes ETH to the most favorable LST automatically.

***

### `redeem(address lst, uint256 esEthAmount, address recipient) → uint256 lstAmount`

Redeem esETH for a supported LST held by the contract.

| Parameter     | Type      | Description                           |
| ------------- | --------- | ------------------------------------- |
| `lst`         | `address` | Address of the desired LST to receive |
| `esEthAmount` | `uint256` | Amount of esETH to burn               |
| `recipient`   | `address` | Address to receive the LST            |

| Returns     | Type      | Description                |
| ----------- | --------- | -------------------------- |
| `lstAmount` | `uint256` | Amount of the LST returned |

| Access       |                                         |
| ------------ | --------------------------------------- |
| **Caller**   | Any (permissionless)                    |
| **Requires** | Caller must hold `esEthAmount` of esETH |

| Reverts | When                                               |
| ------- | -------------------------------------------------- |
| —       | `lst` is not a supported token                     |
| —       | Contract does not hold enough of the requested LST |
| —       | Caller has insufficient esETH balance              |

**Why:** Allows exit from the esETH wrapper back to individual LSTs. Exchange rate is the LST's native rate, not a market price. Redemption depends on contract holdings — if the treasury has rebalanced, a specific LST may have insufficient balance.

***

### `ethPerEsEth() → uint256`

Returns the ETH value of 1 esETH (in wei, 18 decimals). Always returns 1e18 (1:1 peg).

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** The canonical pricing function for esETH. This always returns 1e18 because esETH is pegged 1:1 with ETH — yield from underlying LSTs is harvested separately and does not accrete to the exchange rate. Use this for collateral valuation, lending markets, and portfolio display — never use AMM spot price for fair value.

***

### `previewMint(address lst, uint256 lstAmount) → uint256 esEthAmount`

Preview: returns the esETH amount that would be minted for a given LST deposit.

| Parameter   | Type      | Description        |
| ----------- | --------- | ------------------ |
| `lst`       | `address` | Address of the LST |
| `lstAmount` | `uint256` | Amount of the LST  |

| Returns       | Type      | Description                           |
| ------------- | --------- | ------------------------------------- |
| `esEthAmount` | `uint256` | Equivalent esETH that would be minted |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** Allows UIs and smart contracts to display expected output before executing a mint. Essential for slippage protection and user-facing previews.

***

### `previewRedeem(address lst, uint256 esEthAmount) → uint256 lstAmount`

Preview: returns the LST amount that would be returned for a given esETH redemption.

| Parameter     | Type      | Description               |
| ------------- | --------- | ------------------------- |
| `lst`         | `address` | Address of the LST        |
| `esEthAmount` | `uint256` | Amount of esETH to redeem |

| Returns     | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `lstAmount` | `uint256` | Equivalent LST that would be returned |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** Pre-flight check for redemptions. Critical because redemption depends on contract holdings — `previewRedeem()` reveals whether the requested LST has sufficient balance before executing.

***

### Standard ERC-20 + ERC-2612

esETH implements the full ERC-20 interface with [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612) permit support.

| Function                                                                                                 | Description                                                               |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `balanceOf(address) → uint256`                                                                           | Token balance (non-rebasing — never changes except on transfer/mint/burn) |
| `transfer(address to, uint256 amount) → bool`                                                            | Standard transfer                                                         |
| `approve(address spender, uint256 amount) → bool`                                                        | Standard approval                                                         |
| `transferFrom(address from, address to, uint256 amount) → bool`                                          | Standard delegated transfer                                               |
| `permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` | Gasless approval via off-chain signature                                  |
| `totalSupply() → uint256`                                                                                | Total esETH in circulation                                                |
| `allowance(address owner, address spender) → uint256`                                                    | Current spending allowance                                                |

| Property      | Value                            |
| ------------- | -------------------------------- |
| **Name**      | esETH                            |
| **Symbol**    | esETH                            |
| **Decimals**  | 18                               |
| **Rebasing**  | No                               |
| **Ownership** | Ownable2Step (protocol multisig) |

***

## STRAT

ERC-20 equity token with ERC-2612 permit. Does not rebase. Only minted through convertible note conversion.

| Property      | Value                                                                                                                 |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| **Name**      | ETH Strategy                                                                                                          |
| **Symbol**    | STRAT                                                                                                                 |
| **Decimals**  | 18                                                                                                                    |
| **Address**   | [0x14cF922aa1512Adfc34409b63e18D391e4a86A2f](https://etherscan.io/address/0x14cF922aa1512Adfc34409b63e18D391e4a86A2f) |
| **Standard**  | ERC-20 with ERC-2612 permit                                                                                           |
| **Compiler**  | Solidity v0.8.20, 999,999 optimization runs, Paris EVM                                                                |
| **Inherits**  | MintableBurnableToken → ERC20Permit → Ownable2Step                                                                    |
| **Ownership** | Ownable2Step (protocol multisig)                                                                                      |

### `mint(address to, uint256 amount)`

Mint new STRAT tokens.

| Parameter | Type      | Description                    |
| --------- | --------- | ------------------------------ |
| `to`      | `address` | Recipient of the minted tokens |
| `amount`  | `uint256` | Amount to mint                 |

| Access     |                                                    |
| ---------- | -------------------------------------------------- |
| **Caller** | Authorized minter contracts only (ConvertibleNote) |

| Reverts | When                               |
| ------- | ---------------------------------- |
| —       | Caller is not an authorized minter |
| —       | `to` is the zero address           |

**Why:** STRAT has no inflation schedule and no emission mechanism. New STRAT only enters circulation when a note holder exercises conversion rights. This restricted minting is the fundamental supply guarantee — STRAT supply only grows when someone converts debt to equity.

***

### `burn(uint256 amount)`

Burn STRAT tokens from the caller's balance.

| Parameter | Type      | Description    |
| --------- | --------- | -------------- |
| `amount`  | `uint256` | Amount to burn |

| Access     |                                      |
| ---------- | ------------------------------------ |
| **Caller** | Any holder (burns from `msg.sender`) |

| Reverts | When                            |
| ------- | ------------------------------- |
| —       | Caller has insufficient balance |

**Why:** Public burn function — any holder can destroy their own STRAT. Also used by authorized contracts for protocol operations.

***

### `burnFrom(address from, uint256 amount)`

Burn STRAT tokens from another address using an allowance.

| Parameter | Type      | Description          |
| --------- | --------- | -------------------- |
| `from`    | `address` | Address to burn from |
| `amount`  | `uint256` | Amount to burn       |

| Access     |                                                   |
| ---------- | ------------------------------------------------- |
| **Caller** | Any address with sufficient allowance from `from` |

| Reverts | When                              |
| ------- | --------------------------------- |
| —       | Caller has insufficient allowance |
| —       | `from` has insufficient balance   |

**Why:** Allowance-based burn — used by TreasuryLend to burn STRAT collateral at loan origination. On successful repayment, equivalent STRAT is re-minted. This burn-at-origination model eliminates the need for collateral escrow.

***

### `manageMinter(address minter, bool authorized)`

Add or remove an authorized minter address.

| Parameter    | Type      | Description                            |
| ------------ | --------- | -------------------------------------- |
| `minter`     | `address` | Address to authorize or revoke         |
| `authorized` | `bool`    | `true` to authorize, `false` to revoke |

| Access     |                                                 |
| ---------- | ----------------------------------------------- |
| **Caller** | Owner only (protocol multisig via Ownable2Step) |

**Why:** Governance control over which contracts can create new STRAT. In practice, only the ConvertibleNote contract is an authorized minter. This is checked before every mint operation.

***

### Standard ERC-20 + ERC-2612

| Function                                                                                                 | Description                                                 |
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `balanceOf(address) → uint256`                                                                           | Token balance                                               |
| `transfer(address to, uint256 amount) → bool`                                                            | Standard transfer                                           |
| `approve(address spender, uint256 amount) → bool`                                                        | Standard approval                                           |
| `transferFrom(address from, address to, uint256 amount) → bool`                                          | Standard delegated transfer                                 |
| `permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` | Gasless approval via off-chain signature                    |
| `totalSupply() → uint256`                                                                                | Total STRAT supply — increases only through note conversion |
| `allowance(address owner, address spender) → uint256`                                                    | Current spending allowance                                  |
| `nonces(address owner) → uint256`                                                                        | Permit nonce tracking                                       |

***

## CDT (Convertible Debt Token)

Fungible ERC-20 representing protocol debt. Each CDT = \~$1 of debt. `totalSupply()` = total protocol debt.

{% hint style="info" %}
**Status: Deploying soon.** Address will be published on the [Contracts](/references/contracts.md) page before permissionless launch.
{% endhint %}

| Property      | Value                                                                     |
| ------------- | ------------------------------------------------------------------------- |
| **Name**      | ETH Strategy Debt                                                         |
| **Symbol**    | CDT                                                                       |
| **Decimals**  | 18                                                                        |
| **Standard**  | ERC-20 with ERC-2612 permit                                               |
| **Ownership** | Ownable2Step (protocol multisig)                                          |
| **Minting**   | Restricted to authorized minter contracts (ConvertibleNote, TreasuryLend) |

### `mint(address to, uint256 amount)`

Mint new CDT tokens.

| Parameter | Type      | Description                                 |
| --------- | --------- | ------------------------------------------- |
| `to`      | `address` | Recipient of the minted CDT                 |
| `amount`  | `uint256` | Amount to mint (1 CDT per \~$1 of notional) |

| Access     |                                                                  |
| ---------- | ---------------------------------------------------------------- |
| **Caller** | Authorized minter contracts only (ConvertibleNote, TreasuryLend) |

| Reverts | When                               |
| ------- | ---------------------------------- |
| —       | Caller is not an authorized minter |

**Why:** CDT is minted during convertible note bonding (new debt) and when treasury lending borrowers repay (restoring previously burned CDT). Restricted minting ensures protocol debt cannot be inflated arbitrarily.

***

### `burn(address from, uint256 amount)`

Burn CDT tokens.

| Parameter | Type      | Description          |
| --------- | --------- | -------------------- |
| `from`    | `address` | Address to burn from |
| `amount`  | `uint256` | Amount to burn       |

| Access     |                                                                                                      |
| ---------- | ---------------------------------------------------------------------------------------------------- |
| **Caller** | Authorized contracts (ConvertibleNote burns on conversion/redemption, TreasuryLend burns collateral) |

| Reverts | When                            |
| ------- | ------------------------------- |
| —       | Caller is not authorized        |
| —       | `from` has insufficient balance |

**Why:** CDT is destroyed through three paths: conversion (before expiry), redemption (after expiry), and treasury lending collateral deposit. Each burn reduces total protocol debt.

***

### `manageMinter(address minter, bool authorized)`

Add or remove an authorized minter address.

| Parameter    | Type      | Description                            |
| ------------ | --------- | -------------------------------------- |
| `minter`     | `address` | Address to authorize or revoke         |
| `authorized` | `bool`    | `true` to authorize, `false` to revoke |

| Access     |                                                 |
| ---------- | ----------------------------------------------- |
| **Caller** | Owner only (protocol multisig via Ownable2Step) |

**Why:** Governance control over which contracts can create or destroy CDT. In practice, ConvertibleNote and TreasuryLend are the only authorized minters.

***

### Standard ERC-20 + ERC-2612

| Function                                                                                                 | Description                                                    |
| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `balanceOf(address) → uint256`                                                                           | CDT balance (does not accrue interest)                         |
| `transfer(address to, uint256 amount) → bool`                                                            | Standard transfer                                              |
| `approve(address spender, uint256 amount) → bool`                                                        | Standard approval                                              |
| `transferFrom(address from, address to, uint256 amount) → bool`                                          | Standard delegated transfer                                    |
| `permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` | Gasless approval — enables single-tx conversion and redemption |
| `totalSupply() → uint256`                                                                                | **Total protocol debt in USD terms**                           |
| `allowance(address owner, address spender) → uint256`                                                    | Current spending allowance                                     |

***

## EthStrategyConvertibleNote

Bonding, note issuance, conversion, and redemption. Issues CDT + NFT pairs on bonding; settles them on conversion or redemption.

{% hint style="info" %}
**Status: Deploying soon.** Address will be published on the [Contracts](/references/contracts.md) page before permissionless launch.
{% endhint %}

### `bond(address recipient, uint256 minConversionAmountStrat, uint256 minConversionAmountEth, uint256 deadline) → uint256 tokenId`

Purchase a USD-denominated convertible note by sending ETH. Receives CDT + NFT option.

| Parameter                  | Type      | Description                                                   |
| -------------------------- | --------- | ------------------------------------------------------------- |
| `recipient`                | `address` | Address to receive CDT and NFT                                |
| `minConversionAmountStrat` | `uint256` | Minimum STRAT entitlement (slippage protection)               |
| `minConversionAmountEth`   | `uint256` | Minimum esETH entitlement (slippage protection)               |
| `deadline`                 | `uint256` | Transaction deadline (`block.timestamp` must be ≤ `deadline`) |

| Returns   | Type      | Description             |
| --------- | --------- | ----------------------- |
| `tokenId` | `uint256` | The minted NFT token ID |

| Access       |                               |
| ------------ | ----------------------------- |
| **Caller**   | Any (permissionless, payable) |
| **Requires** | `msg.value > 0`               |

| Reverts                   | When                                                  |
| ------------------------- | ----------------------------------------------------- |
| `NoEthSent`               | `msg.value` is zero                                   |
| `ZeroAddress`             | `recipient` is `address(0)`                           |
| `TransactionStale`        | `block.timestamp > deadline`                          |
| `InsufficientOutput`      | Conversion entitlements fall below specified minimums |
| `InvalidTimelockOrExpiry` | Computed timelock/expiry values are invalid           |

**Why:** The protocol's primary growth mechanism. Notes are USD-denominated — the buyer sends ETH, which is priced in USD via an ETH/USD oracle and acquired for the treasury. The buyer receives composable debt (CDT, 1 per \~$1 USD notional) and conversion rights (NFT) — a structure borrowed from TradFi convertible bonds, decomposed into DeFi primitives. Zero-interest debt is possible because the conversion option itself is the buyer's compensation.

***

### `convertToStrat(uint256 tokenId, uint256 cdtAmount)`

Convert note to STRAT before expiry. Burns CDT, mints STRAT.

| Parameter   | Type      | Description                                        |
| ----------- | --------- | -------------------------------------------------- |
| `tokenId`   | `uint256` | NFT token ID                                       |
| `cdtAmount` | `uint256` | Amount of CDT to burn (partial exercise supported) |

| Access       |                                                          |
| ------------ | -------------------------------------------------------- |
| **Caller**   | `ownerOf(tokenId)` only — NFT approvals are NOT accepted |
| **Requires** | CDT approved or permitted to the contract                |

| Reverts                 | When                                                  |
| ----------------------- | ----------------------------------------------------- |
| `TimelockActive`        | Timelock hasn't passed (\~6.9 days after bonding)     |
| `OptionExpired`         | Past expiry (\~4.2 years) — use `redeem()` instead    |
| `NotOwnerOrApproved`    | Caller is not the NFT owner                           |
| `InvalidExerciseAmount` | `cdtAmount` is 0 or exceeds remaining `amountOwedCdt` |

**Why:** The equity conversion path. The holder exchanges debt for newly minted STRAT, betting on appreciation. Pro-rata esETH backing moves from encumbered to unencumbered holdings, freeing capital for protocol operations — creating a positive flywheel between conversion activity and available lending capital.

***

### `convertToEth(uint256 tokenId, uint256 cdtAmount)`

Convert note to esETH before expiry. Burns CDT, transfers esETH from treasury.

| Parameter   | Type      | Description                                        |
| ----------- | --------- | -------------------------------------------------- |
| `tokenId`   | `uint256` | NFT token ID                                       |
| `cdtAmount` | `uint256` | Amount of CDT to burn (partial exercise supported) |

| Access       |                                           |
| ------------ | ----------------------------------------- |
| **Caller**   | `ownerOf(tokenId)` only                   |
| **Requires** | CDT approved or permitted to the contract |

| Reverts                 | When                                                  |
| ----------------------- | ----------------------------------------------------- |
| `TimelockActive`        | Timelock hasn't passed                                |
| `OptionExpired`         | Past expiry                                           |
| `NotOwnerOrApproved`    | Caller is not the NFT owner                           |
| `InvalidExerciseAmount` | `cdtAmount` is 0 or exceeds remaining `amountOwedCdt` |

**Why:** The risk-off conversion path. The holder exits into ETH-denominated value without selling STRAT on the open market. esETH moves from encumbered → unencumbered → holder's address.

***

### `redeem(uint256 tokenId, uint256 minEthOut)`

Redeem CDT for USD notional value in esETH after expiry.

| Parameter   | Type      | Description                                                            |
| ----------- | --------- | ---------------------------------------------------------------------- |
| `tokenId`   | `uint256` | NFT token ID                                                           |
| `minEthOut` | `uint256` | Minimum esETH to receive (slippage protection against oracle movement) |

| Access     |                         |
| ---------- | ----------------------- |
| **Caller** | `ownerOf(tokenId)` only |

| Reverts              | When                                             |
| -------------------- | ------------------------------------------------ |
| `TimelockActive`     | Timelock hasn't passed                           |
| `OptionUnexpired`    | Note hasn't expired yet — use conversion instead |
| `NotOwnerOrApproved` | Caller is not the NFT owner                      |
| `InsufficientOutput` | esETH output is below `minEthOut`                |

**Why:** The maturity settlement. After \~4.2 years, conversion rights expire and the holder redeems at USD face value. Single-shot: settles the entire remaining position, burns both CDT and NFT. Pro-rata if the protocol is underwater — all CDT holders treated pari passu.

***

### `convertToStratWithPermit(uint256 tokenId, uint256 cdtAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)`

Combine CDT approval and STRAT conversion in one transaction via ERC-2612 permit.

| Parameter     | Type                          | Description               |
| ------------- | ----------------------------- | ------------------------- |
| `tokenId`     | `uint256`                     | NFT token ID              |
| `cdtAmount`   | `uint256`                     | Amount of CDT to burn     |
| `deadline`    | `uint256`                     | Permit signature deadline |
| `v`, `r`, `s` | `uint8`, `bytes32`, `bytes32` | ERC-2612 permit signature |

| Access     |                         |
| ---------- | ----------------------- |
| **Caller** | `ownerOf(tokenId)` only |

**Why:** Gas optimization — saves one transaction by combining CDT approval with conversion. Same mechanics as `convertToStrat()` with an inline permit.

***

### `convertToEthWithPermit(uint256 tokenId, uint256 cdtAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)`

Combine CDT approval and esETH conversion in one transaction.

Same parameters and access control as `convertToStratWithPermit`. Mechanics match `convertToEth()`.

***

### `redeemWithPermit(uint256 tokenId, uint256 minEthOut, uint256 deadline, uint8 v, bytes32 r, bytes32 s)`

Combine CDT approval and post-expiry redemption in one transaction.

Same access control as `redeem()`. Adds permit parameters for gasless CDT approval.

***

### `conversionEntitlements(uint256 ethAmount) → (uint256 stratAmount, uint256 ethEntitlement, uint256 settlementUsd)`

Preview conversion entitlements for a given ETH amount without executing.

| Parameter   | Type      | Description                       |
| ----------- | --------- | --------------------------------- |
| `ethAmount` | `uint256` | Amount of ETH (in wei) to preview |

| Returns          | Type      | Description                  |
| ---------------- | --------- | ---------------------------- |
| `stratAmount`    | `uint256` | STRAT conversion entitlement |
| `ethEntitlement` | `uint256` | esETH conversion entitlement |
| `settlementUsd`  | `uint256` | USD notional (CDT amount)    |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** Essential pre-flight check. Shows exact note terms (in USD) before sending ETH. If the protocol is underwater, `ethEntitlement` will be zero — this function makes that transparent upfront.

***

### `notes(uint256 tokenId) → Note`

Returns the full state of a convertible note.

| Returns (struct)             | Type      | Description                                                     |
| ---------------------------- | --------- | --------------------------------------------------------------- |
| `conversionEntitlementStrat` | `uint256` | STRAT claimable via conversion                                  |
| `conversionEntitlementEth`   | `uint256` | esETH claimable via conversion                                  |
| `settlementEntitlementUsd`   | `uint256` | USD notional for post-expiry redemption                         |
| `amountOwedCdt`              | `uint256` | CDT required for full exercise (decrements on partial exercise) |
| `expiry`                     | `uint256` | Timestamp after which conversion closes                         |
| `timelock`                   | `uint256` | Timestamp before which conversion is blocked                    |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** On-chain transparency for note positions. UIs, dashboards, and integrators query this to display note details, calculate remaining value, and determine which actions are available.

***

### `releaseEncumbrance(uint256 tokenId)`

Move an expired note's backing esETH from encumbered to unencumbered holdings.

| Parameter | Type      | Description                    |
| --------- | --------- | ------------------------------ |
| `tokenId` | `uint256` | NFT token ID (must be expired) |

| Access     |                                |
| ---------- | ------------------------------ |
| **Caller** | Owner only (protocol multisig) |

| Reverts                      | When                                           |
| ---------------------------- | ---------------------------------------------- |
| `EncumbranceAlreadyReleased` | Encumbrance was already released for this note |
| —                            | Note has not expired                           |

**Why:** Administrative function to free protocol liquidity. After a note expires, its backing esETH remains encumbered until released. This allows the protocol to reclaim capital from expired but unredeemed notes without affecting the holder's redemption rights.

***

## StakedStrat (sSTRAT-v2)

On-chain STRAT staking with 7-day anti-frontrunning reward streaming. Accepts STRAT deposits, streams esETH rewards.

{% hint style="info" %}
**Status: Deploying soon.** Address will be published on the [Contracts](/references/contracts.md) page before permissionless launch.
{% endhint %}

| Property            | Value                                                  |
| ------------------- | ------------------------------------------------------ |
| **Receipt Token**   | sSTRAT-v2 (ERC-20 with transfers disabled)             |
| **Reward Token**    | esETH                                                  |
| **Stream Duration** | 7 days                                                 |
| **Reentrancy**      | All state-changing functions guarded by `nonReentrant` |
| **Ownership**       | Ownable2Step (protocol multisig)                       |

### `stake(uint256 amount)`

Stake STRAT tokens. Receives sSTRAT-v2 1:1.

| Parameter | Type      | Description              |
| --------- | --------- | ------------------------ |
| `amount`  | `uint256` | Amount of STRAT to stake |

| Access       |                                                                  |
| ------------ | ---------------------------------------------------------------- |
| **Caller**   | Any (permissionless)                                             |
| **Requires** | Caller must have approved StakedStrat to spend `amount` of STRAT |

| Reverts       | When                                    |
| ------------- | --------------------------------------- |
| `ZeroAmount`  | `amount` is 0                           |
| ERC-20 revert | Insufficient STRAT balance or allowance |

**Why:** Entry point for earning protocol revenue. Staking locks STRAT in the contract and issues a non-transferable receipt. Automatically calls `syncRewards()` before staking to ensure accurate reward accounting.

***

### `unstake(uint256 amount)`

Unstake STRAT tokens. Burns sSTRAT-v2 proportionally.

| Parameter | Type      | Description                |
| --------- | --------- | -------------------------- |
| `amount`  | `uint256` | Amount of STRAT to unstake |

| Access     |                                 |
| ---------- | ------------------------------- |
| **Caller** | Any (must have staked position) |

| Reverts             | When                            |
| ------------------- | ------------------------------- |
| `ZeroAmount`        | `amount` is 0                   |
| `InsufficientStake` | `amount` exceeds staked balance |

**Why:** No lock period — unstaking is always available. Automatically claims all pending esETH rewards before reducing the stake, so nothing is forfeited. The 7-day streaming design makes frontrunning uneconomical without requiring lockups.

***

### `claim()`

Claim accrued esETH rewards without changing staked position.

| Access     |                                 |
| ---------- | ------------------------------- |
| **Caller** | Any (must have staked position) |

**Why:** Allows stakers to collect earned yield without modifying their position. Returns silently if no rewards are pending.

***

### `syncRewards()`

Detect new esETH in the contract and start/extend the reward stream.

| Access     |                      |
| ---------- | -------------------- |
| **Caller** | Any (permissionless) |

**Why:** The mechanism that enables anti-frontrunning. When esETH arrives at the contract, `syncRewards()` detects the new balance and begins a 7-day linear stream. New rewards blend with any active stream using a value-weighted average duration. Called automatically by `stake()`, `unstake()`, `claim()`, and `migrateStake()` — but anyone can call it directly.

***

### `migrateStake(address to, uint256 amount)`

Move staked position to a new address without unstaking.

| Parameter | Type      | Description                          |
| --------- | --------- | ------------------------------------ |
| `to`      | `address` | Destination address                  |
| `amount`  | `uint256` | Amount to migrate (0 = full balance) |

| Access     |                                 |
| ---------- | ------------------------------- |
| **Caller** | Any (must have staked position) |

| Reverts             | When                                                 |
| ------------------- | ---------------------------------------------------- |
| —                   | `to` is zero address, caller, or the contract itself |
| `InsufficientStake` | `amount` exceeds staked balance                      |

**Why:** Wallet rotation without leaving the reward stream. Pays out the recipient's pending rewards during migration to prevent accounting corruption. Migrated rewards are proportional to the migrated stake amount.

***

### `getPendingRewards(address account) → uint256`

View pending (claimable) esETH rewards for an account.

| Parameter | Type      | Description      |
| --------- | --------- | ---------------- |
| `account` | `address` | Address to check |

| Returns | Type      | Description                  |
| ------- | --------- | ---------------------------- |
| —       | `uint256` | Pending esETH rewards in wei |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** Dashboard and UI function. May undercount if new rewards arrived but `syncRewards()` hasn't been called — for accurate readings, call `syncRewards()` first in a static call.

***

## StratETHTreasuryLend

Fixed-rate, fixed-term esETH lending against STRAT + CDT collateral. Positions represented as ERC-721 NFTs.

{% hint style="info" %}
**Status: Roadmap — Q2 2026.** Will be audited and deployed separately from the core protocol launch.
{% endhint %}

| Property           | Value                                                       |
| ------------------ | ----------------------------------------------------------- |
| **Position Token** | ERC-721 (transferable)                                      |
| **Collateral**     | STRAT + CDT (burned at origination)                         |
| **Borrow Asset**   | esETH                                                       |
| **Rate Model**     | Fixed rate, snapshotted at origination                      |
| **Liquidation**    | Time-based only (after expiry) — no price-based liquidation |
| **Reentrancy**     | All state-changing functions guarded by `nonReentrant`      |
| **Ownership**      | Ownable2Step (protocol multisig)                            |

### `borrow(uint256 maxStratIn, uint256 maxCdtIn, uint256 minBorrowAmount, uint256 deadline) → uint256 tokenId`

Open a loan position. Burn STRAT + CDT collateral, receive esETH.

| Parameter         | Type      | Description                                    |
| ----------------- | --------- | ---------------------------------------------- |
| `maxStratIn`      | `uint256` | Maximum STRAT to deposit as collateral         |
| `maxCdtIn`        | `uint256` | Maximum CDT to deposit as collateral           |
| `minBorrowAmount` | `uint256` | Minimum esETH to receive (slippage protection) |
| `deadline`        | `uint256` | Transaction deadline                           |

| Returns   | Type      | Description             |
| --------- | --------- | ----------------------- |
| `tokenId` | `uint256` | The minted position NFT |

| Access       |                                                                          |
| ------------ | ------------------------------------------------------------------------ |
| **Caller**   | Any (permissionless)                                                     |
| **Requires** | Caller must hold sufficient STRAT and CDT, both approved to the contract |

| Reverts              | When                                       |
| -------------------- | ------------------------------------------ |
| `ZeroAmount`         | Either collateral input is zero            |
| `InsufficientOutput` | Computed borrow amount < `minBorrowAmount` |
| `TransactionStale`   | `block.timestamp > deadline`               |

**Why:** Allows STRAT holders to access ETH liquidity without selling, at a fixed rate with no liquidation risk during the term. The interest paid by borrowers flows to STRAT stakers, creating real yield from real demand. Serves as a natural escape valve — any STRAT holder can borrow against NAV-backed value.

***

### `repay(uint256 tokenId)`

Repay a loan before expiry. Returns principal + accrued interest, re-mints STRAT + CDT collateral.

| Parameter | Type      | Description           |
| --------- | --------- | --------------------- |
| `tokenId` | `uint256` | Position NFT token ID |

| Access       |                                                                    |
| ------------ | ------------------------------------------------------------------ |
| **Caller**   | `ownerOf(tokenId)` only                                            |
| **Requires** | Caller must hold sufficient esETH for principal + accrued interest |

| Reverts              | When                                                   |
| -------------------- | ------------------------------------------------------ |
| `LoanExpired`        | `block.timestamp >= expiry` — only liquidation remains |
| `NotOwnerOrApproved` | Caller is not the position NFT owner                   |

**Why:** Early repayment is cheaper — interest accrues linearly, so repaying at the halfway point costs half the maximum interest. Accrued interest flows to stakers; unused interest reserve returns to unencumbered holdings. STRAT and CDT collateral is re-minted to the borrower.

***

### `roll(uint256 tokenId, uint256 additionalStrat, uint256 additionalCdt, uint256 minBorrowAmount, uint256 deadline)`

Roll an existing position: settle accrued interest, adjust collateral, recompute terms, reset term.

| Parameter         | Type      | Description                                        |
| ----------------- | --------- | -------------------------------------------------- |
| `tokenId`         | `uint256` | Position NFT token ID                              |
| `additionalStrat` | `uint256` | Additional STRAT to add (0 to keep same or reduce) |
| `additionalCdt`   | `uint256` | Additional CDT to add (0 to keep same or reduce)   |
| `minBorrowAmount` | `uint256` | Minimum esETH to receive after rolling             |
| `deadline`        | `uint256` | Transaction deadline                               |

| Access     |                         |
| ---------- | ----------------------- |
| **Caller** | `ownerOf(tokenId)` only |

| Reverts              | When                                  |
| -------------------- | ------------------------------------- |
| `LoanExpired`        | Position has already expired          |
| `TransactionStale`   | `block.timestamp > deadline`          |
| `InsufficientOutput` | New borrow amount < `minBorrowAmount` |

**Why:** Avoids the gas and friction of close-then-reopen. Settles accrued interest, adjusts collateral, recomputes terms at current governance parameters (which may have changed), and resets the term clock — all in one transaction, preserving the same NFT.

***

### `liquidate(uint256 tokenId)`

Liquidate an expired position. Permissionless — anyone can call after expiry.

| Parameter | Type      | Description           |
| --------- | --------- | --------------------- |
| `tokenId` | `uint256` | Position NFT token ID |

| Access     |                                         |
| ---------- | --------------------------------------- |
| **Caller** | Any (permissionless, after expiry only) |

| Reverts | When                                              |
| ------- | ------------------------------------------------- |
| —       | `block.timestamp < expiry` (loan not yet expired) |

**Why:** Time-based liquidation — no price oracles, no flash-loan manipulation, no cascading liquidations. On liquidation: full-term interest goes to stakers, delinquent fee goes to unencumbered holdings (increasing ethPerStrat), collateral is forfeited, position NFT is burned. The liquidator receives nothing — liquidation is a public good.

***

### `previewBorrow(uint256 maxStratIn, uint256 maxCdtIn) → (uint256 stratIn, uint256 cdtIn, uint256 ethBacking, uint256 borrowAmount, uint256 maxTermInterest, uint256 delinquentFee)`

Preview loan terms without executing.

| Parameter    | Type      | Description              |
| ------------ | --------- | ------------------------ |
| `maxStratIn` | `uint256` | Maximum STRAT collateral |
| `maxCdtIn`   | `uint256` | Maximum CDT collateral   |

| Returns           | Type      | Description                              |
| ----------------- | --------- | ---------------------------------------- |
| `stratIn`         | `uint256` | Actual STRAT that would be consumed      |
| `cdtIn`           | `uint256` | Actual CDT that would be consumed        |
| `ethBacking`      | `uint256` | Total ETH backing of the deposited STRAT |
| `borrowAmount`    | `uint256` | esETH loan amount                        |
| `maxTermInterest` | `uint256` | Maximum interest over the full term      |
| `delinquentFee`   | `uint256` | Fee reserved for default penalty         |

| Access     |            |
| ---------- | ---------- |
| **Caller** | Any (view) |

**Why:** Pre-flight check showing exact loan terms. The invariant always holds: `borrowAmount + maxTermInterest + delinquentFee = ethBacking`. Essential for UIs to display terms before the user commits collateral.

***

## ESPN (EthStrategyPerpetualNote)

ERC-4626 tokenized vault. Deposit USDS, receive ESPN shares.

| Property          | Value                                                                                                                 |
| ----------------- | --------------------------------------------------------------------------------------------------------------------- |
| **Name**          | ETH Strategy Perpetual Note                                                                                           |
| **Symbol**        | ESPN                                                                                                                  |
| **Standard**      | ERC-4626                                                                                                              |
| **Deposit Asset** | USDS                                                                                                                  |
| **Address**       | [0xb250C9E0F7bE4cfF13F94374C993aC445A1385fE](https://etherscan.io/address/0xb250C9E0F7bE4cfF13F94374C993aC445A1385fE) |
| **Deposit Cap**   | Governance-configurable                                                                                               |
| **Withdrawals**   | May be disabled by governance; check `maxWithdraw()`                                                                  |
| **Compiler**      | Solidity v0.8.20, 999,999 optimization runs, Shanghai EVM                                                             |
| **Inherits**      | ERC4626, Ownable2Step, ReentrancyGuard                                                                                |
| **Ownership**     | Ownable2Step (protocol multisig)                                                                                      |

### Standard ERC-4626 Interface

ESPN follows the standard [ERC-4626 Tokenized Vault](https://eips.ethereum.org/EIPS/eip-4626) specification. If your protocol already supports ERC-4626, no custom integration is needed.

| Function                                                                     | Description                                        |
| ---------------------------------------------------------------------------- | -------------------------------------------------- |
| `deposit(uint256 assets, address receiver) → uint256 shares`                 | Deposit USDS, receive ESPN shares                  |
| `mint(uint256 shares, address receiver) → uint256 assets`                    | Mint exact ESPN shares by depositing USDS          |
| `withdraw(uint256 assets, address receiver, address owner) → uint256 shares` | Withdraw USDS by specifying asset amount           |
| `redeem(uint256 shares, address receiver, address owner) → uint256 assets`   | Redeem ESPN shares for USDS                        |
| `convertToShares(uint256 assets) → uint256`                                  | Preview: USDS → ESPN conversion                    |
| `convertToAssets(uint256 shares) → uint256`                                  | Preview: ESPN → USDS conversion                    |
| `previewDeposit(uint256 assets) → uint256`                                   | Preview shares for a given deposit                 |
| `previewMint(uint256 shares) → uint256`                                      | Preview assets needed to mint shares               |
| `previewWithdraw(uint256 assets) → uint256`                                  | Preview shares burned for a withdrawal             |
| `previewRedeem(uint256 shares) → uint256`                                    | Preview assets received for a redemption           |
| `totalAssets() → uint256`                                                    | Total USDS managed by the vault                    |
| `asset() → address`                                                          | Underlying asset (USDS)                            |
| `maxDeposit(address) → uint256`                                              | Maximum depositable (reflects cap)                 |
| `maxMint(address) → uint256`                                                 | Maximum mintable shares                            |
| `maxWithdraw(address owner) → uint256`                                       | Maximum withdrawable (0 when withdrawals disabled) |
| `maxRedeem(address owner) → uint256`                                         | Maximum redeemable shares                          |

### Owner Functions

| Function                                 | Access  | Description                                                   |
| ---------------------------------------- | ------- | ------------------------------------------------------------- |
| `setManager(address _manager)`           | Owner   | Set the manager address authorized to deploy deposited assets |
| `setDepositCap(uint256 _depositCap)`     | Owner   | Update the maximum total deposits                             |
| `setWithdrawalsDisabled(bool _disabled)` | Owner   | Toggle direct withdrawals on/off                              |
| `increaseAssetsPerShare(uint256 assets)` | Manager | Increase the vault's assets-per-share for yield distribution  |

### Query Functions

| Function                       | Description                             |
| ------------------------------ | --------------------------------------- |
| `manager() → address`          | Current manager address                 |
| `depositCap() → uint256`       | Current deposit cap                     |
| `withdrawalsDisabled() → bool` | Whether direct withdrawals are disabled |

{% hint style="warning" %}
**Withdrawals may be disabled.** Check `maxWithdraw(owner)` before presenting a withdraw button. When direct withdrawals are disabled, users exit via the ESPN/USDS LP on secondary markets or the redemption queue.
{% endhint %}

***

## STRAT Option (StratOption)

ERC-721 representing call options over STRAT. Used for convertible note conversion rights and presale allocations.

| Property      | Value                                                                                                                 |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| **Name**      | STRAT Option                                                                                                          |
| **Symbol**    | oSTRAT                                                                                                                |
| **Standard**  | ERC-721                                                                                                               |
| **Address**   | [0xe1e9093365545e11Cb02c36B2688E17B4Dc447FC](https://etherscan.io/address/0xe1e9093365545e11Cb02c36B2688E17B4Dc447FC) |
| **Compiler**  | Solidity v0.8.20, 999,999 optimization runs, Paris EVM                                                                |
| **Ownership** | Ownable2Step (protocol multisig)                                                                                      |

Each NFT encodes conversion entitlements, timelock, and expiry. The NFT is the "key" that — when combined with CDT — unlocks conversion to STRAT or esETH. Standard ERC-721 transfers and approvals apply, but settlement authorization requires `ownerOf(tokenId)` — approvals are not accepted for conversion/redemption.

### Per-Token Query Functions

| Function                                              | Returns                                                  | Description                                        |
| ----------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------- |
| `strikeAmount(uint256 tokenId) → uint256`             | CDT required to exercise (18 decimals)                   | The "cost" of exercising the option                |
| `notionalUnderlyingAmount(uint256 tokenId) → uint256` | STRAT received on exercise (18 decimals)                 | The equity conversion entitlement                  |
| `notionalUSDAmount(uint256 tokenId) → uint256`        | USD notional value of the note at purchase (18 decimals) | Settlement value for post-expiry redemption        |
| `expiry(uint256 tokenId) → uint256`                   | Expiration timestamp                                     | After this, conversion closes and redemption opens |
| `timelock(uint256 tokenId) → uint256`                 | Earliest exercise timestamp                              | Before this, no conversion or redemption           |

### Management Functions

| Function                                                                                                                                | Access             | Description                                |
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ------------------------------------------ |
| `mint(address to, uint256 strikeAmount, uint256 notionalUnderlyingAmount, uint256 notionalUSDAmount, uint256 expiry, uint256 timelock)` | Authorized minters | Mint a new option NFT with specified terms |
| `burn(uint256 tokenId)`                                                                                                                 | Token owner        | Burn an option NFT                         |
| `manageMinter(address who, bool canMint)`                                                                                               | Owner              | Authorize/revoke minter addresses          |
| `managerRenderer(address renderer)`                                                                                                     | Owner              | Set the tokenURI renderer contract         |

***

## Access Control Summary

All ETH Strategy contracts use [OpenZeppelin's Ownable2Step](https://docs.openzeppelin.com/contracts/4.x/api/access#Ownable2Step) for ownership transfers — a two-step process (propose → accept) that prevents accidental transfers to wrong addresses.

| Contract                   | Owner             | Delegatable Roles                                               |
| -------------------------- | ----------------- | --------------------------------------------------------------- |
| esETH                      | Protocol multisig | —                                                               |
| STRAT                      | Protocol multisig | Minter management (`manageMinter`)                              |
| CDT                        | Protocol multisig | Minter management (`manageMinter`)                              |
| EthStrategyConvertibleNote | Protocol multisig | —                                                               |
| StakedStrat                | Protocol multisig | —                                                               |
| StratETHTreasuryLend       | Protocol multisig | Rate setter, fee setter (can be delegated to automated keepers) |
| ESPN                       | Protocol multisig | —                                                               |

***

## Immutability Guarantees

ETH Strategy contracts are deployed without proxy-upgrade patterns. Deployed code is immutable.

| Governance Can Change (new positions only) | Governance Cannot Change (ever) |
| ------------------------------------------ | ------------------------------- |
| PCF/GCF (bonding price factors)            | Existing note entitlements      |
| Supported LST whitelist for esETH          | Existing note timelock/expiry   |
| Deposit caps                               | Existing loan terms             |
| Reward distribution addresses              | CDT supply accounting           |
| Borrow rate, loan duration (TreasuryLend)  | Contract logic (no upgrades)    |
| Rate setter / fee setter delegation        | Conversion/redemption mechanics |


---

# Agent Instructions: 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:

```
GET https://docs.ethstrat.xyz/developers/contract-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
