# 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](https://docs.ethstrat.xyz/developers/integration-guide). For deployed addresses, see [Contracts](https://docs.ethstrat.xyz/references/contracts).

***

## 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](https://docs.ethstrat.xyz/references/contracts) 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](https://docs.ethstrat.xyz/references/contracts) 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](https://docs.ethstrat.xyz/references/contracts) 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](https://docs.ethstrat.xyz/references/contracts) 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 |
