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

# Overview

## Kairos Protocol Documentation

Kairos is a decentralized interest rate swap AMM. Traders open positions that pay or receive a stream of interest payments indexed to an on-chain rate oracle; LPs supply the collateral that backs the other side of those payments.

This documentation is written for the people who interact with the protocol on-chain:

* **Traders / buyers** — how to open a swap, manage it, settle it at expiry, or exit early.
* **LPs** — how the shared pool works, how shares are priced, and what gates sit in front of deposits and withdrawals.
* **Market creators** — how to create a BUY\_FIXED / BUY\_FLOATING market pair and manage ownership over time.
* **Integrators** — how the `onBehalfOf` authorization pattern lets bundlers, routers, wrappers, and vault adapters act on behalf of end users.
* **Keepers / liquidators** — what to watch for and how to settle or liquidate positions.
* **Indexers** — the events emitted across every lifecycle transition.

## SwapCore

`SwapCore.sol` is the heart of Kairos. It owns the full lifecycle of an interest rate swap: market creation, liquidity provisioning, opening positions, settlement, early exit, and liquidation. Everything a user, LP, keeper, or integrator does on-chain flows through one of its roughly two dozen external functions.

This chapter is a function-by-function reference. Start here for the mental model, then jump to the page that matches your role.

### What SwapCore does

Kairos is a **decentralized interest rate swap AMM**. Traders enter positions that pay or receive a stream of interest payments indexed to an on-chain rate oracle, and LPs supply the collateral that backs the other side of those payments.

Every rate market that SwapCore creates is actually a **pair**:

* **BUY\_FIXED** — the buyer pays a locked-in fixed rate and receives the floating rate (from the reference rate oracle). Useful for hedging variable-rate exposure or speculating that rates will rise.
* **BUY\_FLOATING** — the buyer pays the floating rate and receives a locked-in fixed rate. Useful for locking in a yield or speculating that rates will fall.

Both sides of the pair share the same reference rate oracle, base rate oracle, collateral token, swap term, and leverage multiplier. LPs supply collateral to a single, share-based pool per market; buyers post their own collateral when they open a swap. The pool uses **time buckets** (`numBuckets` × `bucketInterval`) to aggregate mark-to-market P\&L so share prices stay tractable even with many open positions.

Three inputs determine the all-in rate a buyer locks in at `buySwap`:

1. **Base rate** from the base swap rate oracle (tenor-dependent)
2. **Utilization fee** — a kinked curve integrated over the range `[uPre, uPost]` as the swap consumes pool capacity, priced against seasoned (EMA) liquidity rather than the instantaneous pool balance (see [Core concepts → Seasoned utilization fee](/protocol/core-concepts.md#seasoned-utilization-fee))
3. **Risk premium** — an optional oracle-supplied markup, different per side of the pair

### Actors

| Actor                       | What they do                                                                                                                                                                                                                                                                                           | Key functions                                                                                                                                                                                                                                                                      |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Market owner**            | Anyone who calls `createMarket` (paying the optional creation fee) becomes the owner of the resulting pair of markets. The owner can optionally manage an LP whitelist, can terminate the market (stops new swaps), and receives a share of protocol fees if the creator fee is configured in `Admin`. | [`createMarket`](/protocol/market-admin.md#createmarket), [`transferMarketOwnership`](/protocol/market-admin.md#transfermarketownership), [`terminateMarket`](/protocol/market-admin.md#terminatemarket), [`setMarketLpWhitelist`](/protocol/market-admin.md#setmarketlpwhitelist) |
| **Liquidity Provider (LP)** | Deposits the market's collateral token into the pool, receives shares priced at the pool's current mark-to-market value, earns yield from swap fees and settlement inflows.                                                                                                                            | [`supplyCollateral`](/protocol/liquidity.md#supplycollateral), [`withdrawCollateral`](/protocol/liquidity.md#withdrawcollateral)                                                                                                                                                   |
| **Buyer**                   | Opens a swap by posting collateral, holds the position until expiry, and receives the net payment (or pays it) at settlement. Can transfer or (if enabled) exit early.                                                                                                                                 | [`buySwap`](/protocol/swaps.md#buyswap), [`transferSwapPosition`](/protocol/swaps.md#transferswapposition), [`exitSwapEarly`](/protocol/swaps.md#exitswapearly), [`claimEscrow`](/protocol/swaps.md#claimescrow)                                                                   |
| **Keeper**                  | Permissionless role. Settles swaps at expiry in batches to keep the pool's accounting current. Anyone can do this — there is no keeper whitelist.                                                                                                                                                      | [`makePayment`](/protocol/swaps.md#makepayment), [`updateMarketRateIndex`](/protocol/views.md#updatemarketrateindex)                                                                                                                                                               |
| **Liquidator**              | Permissionless role. Calls `liquidateSwap` on an underwater position to recover the shortfall and collect the market's `liquidationIncentive`.                                                                                                                                                         | [`liquidateSwap`](/protocol/swaps.md#liquidateswap)                                                                                                                                                                                                                                |
| **Integrator / bundler**    | Contracts that act on behalf of end users (vault adapters, wrappers, routers). Must be pre-authorized by the end user via `setAuthorization` for any `onBehalfOf` call.                                                                                                                                | [`setAuthorization`](/protocol/authorization.md#setauthorization), all `onBehalfOf` entry points                                                                                                                                                                                   |

### End-to-end lifecycle

A swap's life runs through five phases. The function names below link to their reference entries.

#### 1. Market setup

The market owner calls [`createMarket`](/protocol/market-admin.md#createmarket) with oracle addresses, collateral token, leverage, swap term, fee-curve parameters, bucket configuration, and optional whitelist flag. SwapCore deploys **both** sides of the market pair (BUY\_FIXED + BUY\_FLOATING) atomically, charges the protocol's market creation fee (if configured in `Admin`), and initializes the pool, buckets, and rate index for each side.

Ownership can be handed off later via [`transferMarketOwnership`](/protocol/market-admin.md#transfermarketownership) + [`acceptMarketOwnership`](/protocol/market-admin.md#acceptmarketownership) (2-step), or the market can be permanently closed to new swaps via [`terminateMarket`](/protocol/market-admin.md#terminatemarket). If [`terminateMarket`](/protocol/market-admin.md#terminatemarket) is called, existing swaps continue to expiry as normal.

#### 2. LP provisioning

LPs call `supplyCollateral` to deposit the market's collateral token. Shares are minted at the current mark-to-market share price (floor `WAD` for an empty pool, `getPoolSharePrice(marketId)` otherwise).

Three gates sit in front of a deposit:

* **1-block deposit lock** — `lpPosition.lastDepositBlock` is stamped on every deposit; a withdrawal in the same block reverts. Prevents same-block deposit+withdraw flash-loan attacks.
* **Underwater floor** — deposits are blocked when the pool is at `MIN_SHARE_PRICE` with active swaps (`lockedCollateral > 0`), so new LPs aren't instantly diluted into an underwater pool.
* **Oracle floor** — for `SpotRate` / `SpotCompoundRate` markets, deposits revert if the reference oracle index is at its floor (`MIN_INDEX`). Cumulative oracles are monotonic and unaffected.

**Profit vest (LP share-price cap)**

Every deposit records a **profit-vest anchor** on the LP's position: `entryPrice` (the share price at deposit) and `vestEndTimestamp` (`block.timestamp + admin.lpProfitVestSeconds()`, default 12h, bounded to `[1h, 7d]` and timelocked). While the vest window is active, `withdrawCollateral` burns the LP's shares at `min(currentSharePrice, entryPrice)` instead of the live share price. This neutralizes the canonical **deposit → oracle-update → withdraw** NAV-sandwich attack on LP shares.

The cap has three defining properties:

* **One-directional.** It binds *only* when the live share price is above the anchor. Below-anchor exits realize the full loss — the vest is not a put option.
* **Uniform across the position.** Every share in a single LP position prices at the capped rate while the window is active; there is no per-tranche accounting. The anchor never ratchets *upward*, so a mid-vest top-up at a price **at or above** the anchor leaves it unchanged. A top-up **below** the anchor blends it *downward* to a share-weighted average of the old anchor and the current price, so the new capital can't shelter under a stale high mark. The anchor is only fully reset to the current price when the prior window has expired or the position has been completely exited. A consequence: topping up at a price above the anchor realizes a haircut on the new shares if withdrawn before vest expiry — holding past `vestEndTimestamp` restores full value.
* **Always extended.** Each new deposit pushes `vestEndTimestamp` out by the current `lpProfitVestSeconds`, giving the latest capital a full protection window. Existing positions keep their original window across admin config changes.

Off-chain surfaces should read `getLpVestState(marketId, lp)` → `(entryPrice, vestEndTimestamp, capActive)` on the **`Views`** contract (a separate read-only helper deployed alongside SwapCore — not a SwapCore method) and warn an LP before they supply or withdraw while `capActive` is `true`.

**Withdrawals**

Withdrawals happen through `withdrawCollateral`, which burns shares and transfers the proportional amount of underlying collateral (capped per the profit vest above). Passing a withdrawal `amount` at or above the position's full value is treated as a full exit. Several guards apply:

* The withdrawal is **blocked if the pool has expired unsettled swaps** (the share price would be stale until they're settled).
* When the pool is idle (`lockedCollateral == 0`), the share price is computed directly from `totalCollateral / totalShares` and the oracle is bypassed entirely — LP withdrawals stay available even if the reference oracle is reverting.
* A **last-LP guard** prevents `pool.totalShares` from reaching zero while `lockedCollateral > 0`: a single share is retained so the pool's accounting stays alive for any remaining active swaps.
* If a full last-LP exit on an idle pool would otherwise sweep cap-protected surplus, the call reverts — the LP can wait for vest expiry or make a partial withdrawal instead.

The `CollateralTokenWithdrawn` event carries a `capApplied` flag indicating whether the profit-vest cap bound on that withdrawal.

#### 3. Opening a swap

A buyer calls `buySwap` with the target market, notional amount, an `onBehalfOf` owner, and **three slippage guards**:

* `rateBound` — for BUY\_FIXED, a ceiling on the locked-in `swapRate` (reverts if the rate would be higher); for BUY\_FLOATING, a floor on the `baseRate` received (reverts if it would be lower). Pass `type(int256).max` to disable.
* `maxMarkup` — ceiling on `utilFee + riskPremium` (in WAD). Pass `0` to disable.
* `maxTotalIn` — ceiling on the **total tokens pulled from the caller** (`requiredBuyerCollateral + protocolFee + liquidationBounty`). Pass `type(uint256).max` to disable. This guard is necessary because buyer collateral is sized off `abs(baseRate)`, so a *favorable* `rateBound` check (a BUY\_FLOATING with a high base rate, or a BUY\_FIXED with a negative base rate) can still inflate the collateral posted — `rateBound` alone does not bound the tokens spent.

SwapCore reads the base rate (tenor-aware) and reference-rate index from their oracles, computes the utilization fee by integrating the kinked fee curve across the capacity the new swap consumes, and reads the risk premium (`0` if no oracle is set). For **BUY\_FIXED**, these combine into the final locked-in `swapRate` (`base + utilFee + riskPremium`); for **BUY\_FLOATING**, `swapRate` is left at `0` — the floating leg isn't known until settlement, so the rate is derived from the entry index versus the expiry index.

It then derives the required **buyer collateral** (sized off `abs(baseRate)` plus fees, to cover the buyer's maximum payment) and **LP collateral backing** (sized off `baseRate`, to cover the LP's maximum payment), and checks that both are at least `minCollateral` (dust guard) and that the pool has enough available collateral. After the slippage checks pass, it:

1. Pulls `requiredBuyerCollateral + protocolFee + liquidationBounty` from `msg.sender` in a single transfer.
2. Routes the protocol fee to the protocol multisig, splitting off the creator-fee share to the market owner if configured. Both transfers are best-effort so a bad recipient never bricks the swap: if the market owner can't receive, its share falls back to the multisig; if the multisig can't receive, the protocol share is refunded to the payer and a `ProtocolFeeForegone` event is emitted rather than the fee being held on the contract.
3. Reserves the LP backing by moving it from the pool's available balance into `pool.lockedCollateral`.
4. Prefunds the `liquidationBounty` (carved out of the buyer's posted amount and tracked separately, so bucket P\&L stays accurate). It is returned to the buyer at normal expiry if the swap is never liquidated.

The new swap is assigned to a time bucket (`(entryTimestamp / bucketInterval) % numBuckets`) and appended to the market's `expiryQueue`, so future share-price calculations only need to iterate over buckets instead of all open swaps. A `SwapCreated` event is emitted with the full entry snapshot (rates, collateral both sides, util fee, risk premium, protocol fee, liquidation bounty, entry index).

#### 4. Swap management (during the term)

* [`getSwapNetAmount`](/protocol/views.md#getswapnetamount) / [`getFreshSwapNetAmount`](/protocol/views.md#getfreshswapnetamount) — query the net payment obligation at any time.
* [`updateMarketRateIndex`](/protocol/views.md#updatemarketrateindex) — permissionlessly refresh the on-chain rate index. Anyone can call this before reading P\&L or settling.
* [`transferSwapPosition`](/protocol/swaps.md#transferswapposition) — reassign ownership of an open swap to another address.

#### 5. Swap closure

Three paths take a swap to `settled = true`. All three release the locked collateral, zero out the bucket slot, and advance the market's `expiryQueuePointer` past the now-settled entry. All three emit `SwapClosed` (the unified indexer-friendly closure event); liquidation additionally emits `SwapLiquidated` with the liquidator-specific details.

* **Normal expiry —** [**`makePayment`**](/protocol/swaps.md#makepayment)**.** Permissionless. Called after `entryTimestamp + swapTerm`. Computes the fixed and floating legs over the full term, nets them, and transfers the net to the winner. If the buyer's token transfer fails (e.g. blacklisted address), the payout is held in `escrowedCollateral[swapId]` and can be pulled later via [`claimEscrow`](/protocol/swaps.md#claimescrow). `makePayment` accepts an array of swap IDs — keepers batch.
* **Early exit —** [**`exitSwapEarly`**](/protocol/swaps.md#exitswapearly)**.** Only the swap owner (or an authorized delegate). Requires `market.earlyExitAllowed = true`. Settles using the current index, applies the market's `earlyExitFee`, and enforces per-swap `minExitAmount` slippage protection.
* **Liquidation —** **`liquidateSwap`.** Permissionless. Triggered when **accrued** (not projected) payments exceed the liquidatable side's available collateral — the buyer's collateral balance on the buyer side, or, on the pool side, the LP's pool backing **net of the liquidation incentive** (`poolBacking × (1 − liquidationIncentive)`), so the incentive is always coverable. Buyer-side liquidations pay the prefunded `liquidationBounty` to the liquidator; pool-side liquidations pay `liquidationIncentive` × the swap's pool backing.

After closure the buyer can call [`claimEscrow`](/protocol/swaps.md#claimescrow) if their settlement transfer failed. Funds always go back to the original `swap.userAddress` — no admin can redirect them.

### Happy-path sequence

```mermaid
sequenceDiagram
    participant Owner as Market Owner
    participant LP
    participant Buyer
    participant SC as SwapCore
    participant Keeper

    Owner->>SC: createMarket(...)
    SC-->>Owner: (fixedMarketId, floatingMarketId)

    LP->>SC: supplyCollateral(marketId, amount, lp, minShares)
    SC-->>LP: CollateralSupplied

    Buyer->>SC: buySwap(marketId, notional, buyer, rateBound, maxMarkup, maxTotalIn)
    SC-->>Buyer: SwapCreated (swapId)

    Note over SC: swap accrues P&L against oracle index

    Keeper->>SC: updateMarketRateIndex(marketId)
    Keeper->>SC: makePayment([swapId])
    SC-->>Buyer: net payment (or LP, depending on direction)
    SC-->>Keeper: SwapClosed
```

### Data structures you'll see in this chapter

All defined in `Types.sol`:

* **`Market`** — full market config: oracles, pool, fee curve, risk settings, whitelist flag, market owner, termination flag
* **`Pool`** — `(totalShares, totalCollateral, lockedCollateral)`
* **`SwapPosition`** — the swap record: `marketId`, `userAddress`, `settled` + `isEarlyExit` flags, `collateralBalance`, `notionalAmount`, rates (`baseRate` + `swapRate`, both signed; `swapRate` is `0` for BUY\_FLOATING and derived at settlement), `entryTimestamp`, `entryFloatingIndex` (the entry index), `poolCollateralBacking`, `utilFee`, `riskPremium`, `liquidationBounty`
* **`LpPosition`** — `(shares, lastDepositBlock, entryPrice, vestEndTimestamp)` — the last two are the profit-vest anchor and window end (see LP provisioning)
* **`Bucket`** — per-bucket aggregates, in struct order: `lpNotional`, `weightedLpRate` (signed), `weightedEntryTime`, `weightedInverseIndex` (harmonic-mean term for the floating rate), `weightedUtilFee`, `weightedRiskPremium`, `totalBuyerCollateral`, `totalPoolBacking`, and `weightedFeeTime` (the fee×entry-time term for exact MTM accrual)
* **`ExitRequest`** — `(swapId, minExitAmount)` input to `exitSwapEarly`
* **`SettlementResult`** — `(settlementAmount, netRecipient)` returned by `makePayment` / `exitSwapEarly`. `settlementAmount` is the net obligation in swap-token decimals; `netRecipient` is a `uint8` direction flag — `0` means the LP pool receives, `1` means the buyer receives.
* **`RateConvention`** — how the reference rate oracle reports data: `Cumulative`, `SpotRate`, or `SpotCompoundRate`
* **`RateType`** — `BUY_FIXED = 0`, `BUY_FLOATING = 1`

### Chapter layout

| Page                                               | Covers                                                                                                                           |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| [Market administration](/protocol/market-admin.md) | `createMarket`, `transferMarketOwnership`, `acceptMarketOwnership`, `terminateMarket`, `setMarketLpWhitelist`, `getAllMarketIds` |
| [Liquidity provision](/protocol/liquidity.md)      | `supplyCollateral`, `withdrawCollateral`, and all LP/pool view functions                                                         |
| [Swaps](/protocol/swaps.md)                        | `buySwap`, `makePayment`, `exitSwapEarly`, `liquidateSwap`, `transferSwapPosition`, `claimEscrow`                                |
| [Authorization](/protocol/authorization.md)        | `setAuthorization`, `isAuthorized`, and the `onBehalfOf` pattern                                                                 |
| [Views](/protocol/views.md)                        | Read-only P\&L, liquidity, oracle-index, and mapping getters                                                                     |
| [Events](/protocol/events.md)                      | Integrator-facing events emitted across the lifecycle                                                                            |


---

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

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kairosswap.com/protocol/overview.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
