> For the complete documentation index, see [llms.txt](https://docs.kyte.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kyte.sh/protocol/contracts.md).

# Smart contracts

The immutable contracts behind Kyte, function by function.

{% hint style="success" %}
**In short.** Every core contract is **immutable**: no proxies, no upgrades. The settings that can change are owned by the timelock and bounded by hard-coded caps. Calls and puts live in separate markets with separate escrows.
{% endhint %}

All core contracts are **immutable** (no proxies). Settings that can change are owned by the timelock.

## AssetRegistry

Token whitelist. v1 holds at most 25 assets, v2 at most 300.

| Function                                                                                    | Access                                           | Notes                                             |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------- |
| `addAsset(token, cap, minStrike)`                                                           | Timelock                                         | Lists and enables a token; reads its `decimals()` |
| `addAssets(tokens[], caps[], minStrikes[], putsOn)`                                         | Timelock                                         | Batch listing (v2), optionally with puts on       |
| `setEnabled(token, bool)`                                                                   | Enable: timelock · Disable: guardian or timelock | Disabling blocks new offers only                  |
| `setPutsEnabled(token, bool)`                                                               | Enable: timelock · Disable: guardian or timelock |                                                   |
| `setCap(token, cap)` / `setMinStrike(token, minStrike)`                                     | Timelock                                         |                                                   |
| `isEnabled`, `putsEnabled`, `cap`, `minStrike`, `getAsset`, `assetCount`, `listedTokens(i)` | View                                             | `putsEnabled` requires both flags                 |

## OptionNFT

ERC-721, one token per filled option. `setMarkets(call, put)` can be called once, by the deployer. Only the matching market can mint or burn ids with its tag (`marketOf(id)`). Uses `_mint`, not `_safeMint`, so there are no receiver callbacks. `tokenURI` returns on-chain SVG and metadata.

## OptionMarketBase (shared by CallMarket and PutMarket)

| Function                                                                                                    | Access                                         | Paused? |
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ------- |
| `buy(id, maxPremium)`                                                                                       | Anyone                                         | Blocked |
| `buyFor(id, maxPremium, beneficiary)`                                                                       | Router only                                    | Blocked |
| `cancelUnfilled(id)`                                                                                        | Seller                                         | Works   |
| `expire(id)`                                                                                                | Anyone                                         | Works   |
| `expireMany(ids[])`                                                                                         | Anyone, ≤ 50                                   | Works   |
| `getPosition(id)`, `idOf(localId)`, `locked(token)`, `exposure(token)`, `openOffers(seller)`, `lastLocalId` | View                                           |         |
| `setFees(premiumBps, exerciseBps)`                                                                          | Timelock, within hard caps; new positions only |         |
| `setRouter(router)`                                                                                         | Timelock                                       |         |
| `pause()` / `unpause()`                                                                                     | Guardian or timelock / timelock only           |         |

**Position struct** (packed into 5 storage slots):

```solidity
struct Position {
    address seller;  uint64 expiry;  uint32 feeBpsPremium;        // slot 0
    address token;   uint64 offerDeadline; uint16 feeBpsExercise; Status status; // slot 1
    uint256 amount;  // underlying tokens
    uint256 strike;  // total WETH on exercise
    uint256 premium; // total WETH to fill
}
```

## CallMarket

* `createOffer(token, amount, strike, premium, expiry, offerDeadline)`: escrows `amount` tokens.
* `exercise(id)`: holder pays `strike` in WETH (approve first) and receives the tokens.
* `exerciseFor(id, holder)`: Router only. Router pays wrapped ETH, and the market still checks that `holder` owns the NFT.

## PutMarket

* `createPutOffer(...)`: escrows `strike` WETH from the caller.
* `createPutOfferFor(seller, ...)`: Router only. Escrows WETH the Router wrapped from the seller's ETH.
* `exercisePut(id)`: holder delivers exactly `amount` tokens (approve first) and receives `strike − fee` in WETH.

CallMarket and PutMarket are separate contracts, so each keeps its own escrow.

## Router

Stateless ETH periphery. Wraps exactly what's needed, refunds the excess, and reverts if it ends with any ETH or WETH balance (`RouterHoldsBalance`).

| Function                                                                  | `msg.value` must cover |
| ------------------------------------------------------------------------- | ---------------------- |
| `buyWithETH(id, maxPremium)`                                              | premium                |
| `buyPutWithETH(id, maxPremium)`                                           | premium                |
| `exerciseWithETH(id)`                                                     | strike                 |
| `createPutOfferWithETH(token, amount, strike, premium, expiry, deadline)` | strike                 |

## FeeVault

Receives every fee in WETH. `split()` can be called by **anyone**, at most once per epoch, while there is a balance and the destinations are set. It works while paused. Rounding dust stays for the next split.

| Function                                                                               | Access                                               |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `split()`                                                                              | Anyone                                               |
| `setDestinations(stakingPool, buybackBurner, opsSafe)`                                 | Timelock                                             |
| `setSplit(stakers, buyback, ops)`                                                      | Timelock; must sum to 10,000 bps and stakers ≥ 2,000 |
| `currentEpoch()`, `lastSplitEpoch`, `totalSplit`, `stakersBps`, `buybackBps`, `opsBps` | View                                                 |

## StakingPool

Stake `$KYTE` and earn WETH. See [Staking mechanics](/protocol/staking.md).

| Function                                                                                                                                               | Access              | Paused? |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------- |
| `stake(amount)`                                                                                                                                        | Anyone              | Blocked |
| `unstake(amount)`                                                                                                                                      | Staker              | Works   |
| `claim()`                                                                                                                                              | Staker              | Works   |
| `notifyReward(amount)`                                                                                                                                 | FeeVault only       |         |
| `setMinClaim(x)`                                                                                                                                       | Timelock, ≤ 0.1 ETH |         |
| `balancesOf`, `lockedOf`, `pendingRewards`, `stakeOf`, `currentEpoch`, `epochEndsAt`, `activeSupply`, `pendingSupply`, `undistributed`, `totalClaimed` | View                |         |

## BuybackBurner

Holds the buyback share. Its WETH is spent only by `buyAndBurn`, through the timelock-set adapter, and the `$KYTE` it receives is burned in the same transaction.

| Function                                                                  | Access                            |
| ------------------------------------------------------------------------- | --------------------------------- |
| `buyAndBurn(wethIn, minTokenOut, deadline)`                               | Keeper only; blocked while paused |
| `setKeeper`, `setAdapter`, `setBounds(maxPerCall, maxSlippageBps ≤ 2000)` | Timelock                          |
| `lastPrice`, `totalWethIn`, `totalBurned`, `maxPerCall`, `maxSlippageBps` | View                              |

Burns by calling `token.burn()`, or by transferring to `0x…dEaD` if the token has no burn function.

## UniswapV4Adapter

Swaps WETH → `$KYTE` directly on the Uniswap v4 PoolManager (`unlock → swap → settle/take`) and refunds any partial fill. `quoteWethForToken` uses the **KyteHook 10-minute TWAP** and never falls back to spot: if the TWAP can't be read, the quote reverts and `buyAndBurn` does not run. `quoteSpot` exposes the spot quote for comparison.

## KyteHook

A Uniswap v4 hook on the `$KYTE`/WETH pool. Details in [Uniswap v4 and KyteHook](/protocol/uniswap-v4.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.kyte.sh/protocol/contracts.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
