> 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/views.md).

# Views

## Views

Read-only paths into SwapCore, plus the standalone `Views` lens contract that batches and previews SwapCore state for UIs and indexers. Everything on this page is safe for off-chain consumers (frontends, indexers) to call via `staticCall`, with the one documented exception.

Source: `SwapCore.sol` (core reads and auto-generated getters) and `lib/Views.sol` — a full read-only lens contract (\~25 external functions: batched market/LP/oracle reads and exact supply/withdraw previews) deployed at its own address, wrapping SwapCore's public surface. Call lens functions against the `Views` address, everything else against SwapCore.

### Why two flavors of P\&L reads?

SwapCore stores a **cumulative rate index** per oracle (`rateIndex`) that is only written when a state-changing function runs — `buySwap`, `supplyCollateral`, `withdrawCollateral`, `makePayment`, `exitSwapEarly`, `liquidateSwap`, or the dedicated `updateMarketRateIndex`. Between those updates, a pure view read (`getSwapNetAmount`) projects a fresh index on the fly (via `getFreshIndex`) so the answer still reflects time elapsed since the last write. For consumers who want the **stored** index actually written to storage first, `getFreshSwapNetAmount` runs the update as a side effect, then returns the value — useful when a frontend wants to `staticCall` a mutable function and know the same number would be used by a subsequent `makePayment`.

See also [`updateMarketRateIndex`](#updatemarketrateindex) for the standalone index-refresh path.

***

## SwapCore — swap valuation

### getSwapNetAmount

```solidity
function getSwapNetAmount(bytes32 swapId)
    public view returns (uint256 netAmount, uint8 netRecipient);
```

**Who calls:** anyone. Pure `view`.

**What it does:** computes the **current net payment obligation** of an open swap — how much one side would owe the other if it settled right now. Projects the reference rate oracle forward with `getFreshIndex` (no state mutation) to account for time elapsed since the last index write, then nets the accrued fixed and floating legs.

**Mid-term this is an accrued-only valuation, not a settlement simulation.** It shares its basis with the liquidation gate (`Utils.isSwapLiquidatable`), so the view always agrees with what a liquidation would trigger on. Only for an **expired** swap (`timeElapsed == swapTerm`) does the accrued value coincide exactly with the full-term math `makePayment` runs at settlement.

Steps:

1. Load the swap and market. Revert if the swap doesn't exist (`E500`) or the market is gone (`E300`).
2. Compute `timeElapsed`, capped at `swapTerm`. If no time has elapsed (`entryTimestamp == block.timestamp`), return `(0, 1)` — no obligation, buyer is the trivial recipient.
3. Resolve the effective oracle index:
   * **Active swap:** use `getFreshIndex(oracle)` — a projected-forward read.
   * **Expired swap**, in order:
     1. If a post-expiry snapshot exists, use the stored interpolation via `rateIndex.getIndexAt(expiry)` — the same bracket settlement would use.
     2. Otherwise, try `getFreshIndex` and interpolate the fresh value back to `expiryTimestamp` via `getIndexAtVirtual` — bit-equivalent to what a same-transaction settle would resolve, so a stale view can't mask a pending settlement.
     3. If the fresh oracle read itself reverts (dead/invalid oracle): fall back to the stored `getIndexAt(expiry)` **only if** the last stored update is at or after expiry; with no maturity-bracketing snapshot at all, revert `E450`.
4. Derive `floatingPaymentRate` from `(entryFloatingIndex, effectiveIndex, rateDuration)` using `SwapFormulas.deriveRateFromIndex`.
5. Compute the accrued legs. The fixed leg comes from `SwapFormulas.accruedFixedLeg` using `swap.baseRate` plus the **stored** `swap.utilFee` / `swap.riskPremium` (re-added explicitly on the fixed leg for **BUY\_FIXED**; on the floating leg for **BUY\_FLOATING**). `swap.swapRate` is never read here. On the Cumulative BUY\_FIXED side the fixed leg accrues on the **compounded** curve implied by the stored full-tenor rate (matching the convex floating leg — straight-line proration would overstate the fixed leg mid-term); every other convention/side accrues straight-line. At expiry the compounded value collapses to the straight line.
6. Net them with `SwapFormulas.calculateNetObligation(fixedPayment, floatingPayment)`. Flip the `buyerOwes` bit if this is a BUY\_FLOATING market (the buyer's side of the swap is the floating leg, not the fixed one).
7. **Cap by collateral.** If the buyer is the one receiving, the net is capped at `swap.poolCollateralBacking`. If the buyer is the one paying, it's capped at `swap.collateralBalance`. This mirrors exactly what `makePayment` and `liquidateSwap` would do.

**Returns:**

| Field          | Meaning                                                          |
| -------------- | ---------------------------------------------------------------- |
| `netAmount`    | The net obligation in `swapToken` decimals. Always non-negative. |
| `netRecipient` | `0` = LP pool receives, `1` = buyer receives.                    |

**Reverts:**

| Code   | Reason                                                                                         |
| ------ | ---------------------------------------------------------------------------------------------- |
| `E500` | Swap doesn't exist.                                                                            |
| `E300` | Market doesn't exist (swap references a purged market — shouldn't happen in normal operation). |
| `E606` | Reference rate oracle returned invalid data during `getFreshIndex`.                            |
| `E725` | Oracle rate/decimals normalization out of bounds during `getFreshIndex` (misconfigured feed).  |
| `E450` | Expired swap, oracle unavailable, and no stored snapshot at or after expiry (step 3.3 above).  |

**Notes:**

* `E450` fires here **even past the settlement grace period, where `makePayment` itself would fallback-settle** on extrapolated data. The view deliberately stays fail-closed: settle the position (permissionless) to realize its value in that state.
* Returns `(0, 1)` for a swap where `block.timestamp == entryTimestamp` — useful sentinel for "just-opened".
* **Both return fields are only meaningful while the swap is open.** Once `swap.settled == true`, the net-computing branch is skipped, so this function returns the sentinel `(0, 1)` — `netAmount` defaults to `0` and `netRecipient` defaults to `1` ("buyer receives") regardless of who actually won. (Even that is best-effort: the index-resolution prelude runs *before* the settled check, so a settled swap can still revert `E450` or propagate `getFreshIndex` failures.) Do **not** read either field on a settled swap. To recover settlement data after the fact:
  * **Direction + net amount** (who received the net, and how much) → the `SwapClosed` event's `netRecipient` and `amountSettled` fields (emitted by every closure path; also returned synchronously as `SettlementResult` from `makePayment` / `exitSwapEarly`). There is no storage getter for `netRecipient` — it lives only in the event/return.
  * **The buyer's remaining on-core claim** → [`getBuyerSettlementValue`](#getbuyersettlementvalue) in one call, or the raw slots [`settlementPayouts`](#settlementpayouts) / [`escrowedCollateral`](#escrowedcollateral). A losing buyer can still have a nonzero payout, so you can't infer `netRecipient` from any of these.

**See also:** [`getFreshSwapNetAmount`](#getfreshswapnetamount), [`getBuyerSettlementValue`](#getbuyersettlementvalue), and the [`SwapClosed`](/protocol/events.md#swapclosed) event for post-settlement direction and net amount.

***

### getFreshSwapNetAmount

```solidity
function getFreshSwapNetAmount(bytes32 swapId)
    external returns (uint256 netAmount, uint8 netRecipient);
```

**Who calls:** frontends via `eth_call` / `staticCall`. **Not marked `view`** — this function mutates the global rate index.

**What it does:**

1. Reads `swap.marketId`.
2. Calls `rateIndex.update(market.referenceRateOracle)` — a real storage write under normal execution.
3. Delegates to `getSwapNetAmount(swapId)`.

The only reason this exists is so a frontend can `staticCall` a mutable function and receive the exact number `makePayment` would use in the next block, with zero divergence from the stored index. When called via `staticCall`, the index update is rolled back at the end of the simulation, so consumers get the fresh number without persisting the write.

**⚠️ Do NOT call this on-chain.** Every call performs a `rateIndex.update`, which is wasted gas if you're already about to call a state-changing function (that function will update the index itself). On-chain callers should use `getSwapNetAmount` directly.

**Returns:** same as `getSwapNetAmount`.

**Reverts:** same as `getSwapNetAmount`, plus any revert from `rateIndex.update` (which itself reverts with `E606` if the oracle is unhealthy). One ordering quirk: a **nonexistent `swapId`** reverts `E608` (uninitialized index), not `E500` — the zero `marketId` resolves to a zero oracle address, and `rateIndex.update` fails on it before `getSwapNetAmount`'s existence check is reached.

**See also:** [`getSwapNetAmount`](#getswapnetamount), [`updateMarketRateIndex`](#updatemarketrateindex).

***

### getBuyerSettlementValue

```solidity
function getBuyerSettlementValue(bytes32 swapId) public view returns (uint256 buyerValue);
```

**Who calls:** anyone — the one-call read for "what would the buyer walk away with if settlement ran this block".

**What it does:**

* **Unsettled, live, early-exitable swap:** returns the more conservative of the liquidation-aware accrued value and the immediate early-exit closeout value, so an adverse remaining-term base-rate move is reflected.
* **Unsettled, expired swap:** returns the exact settlement-equivalent value — remaining collateral + refunded liquidation bounty + any pool-side receipt (no liquidator can fire after expiry).
* **Settled (but still tracked) swap:** returns `escrowedCollateral[swapId]` — the failed-transfer escrow is the only remaining on-core buyer claim; a successful payout has already left to the recipient's balance. This branch reads a single packed slot, so callers can value every tracked swap in one call without decoding the full `swapPositions` struct.

**Reverts:** like `getSwapNetAmount` — `E450` for an expired unsettled swap with no maturity-bracketing snapshot, even past the settlement grace period where `makePayment` would fallback-settle. Settle the position to realize its value in that state.

**See also:** [`settlementPayouts`](#settlementpayouts), [`escrowedCollateral`](#escrowedcollateral), [`claimEscrow`](/protocol/swaps.md#claimescrow).

***

## SwapCore — pool & vesting reads

For the three share prices themselves (`getPoolSharePrice`, `getPoolBurnPrice`, `getVirtualSharePrice`) and LP position reads (`getLpValue`, `getLpPosition`, `getLpShares`, `totalLPAvailableCollateral`), see [Liquidity provision](/protocol/liquidity.md).

### getPoolMintAndBurnPrice

```solidity
function getPoolMintAndBurnPrice(bytes32 marketId)
    external view returns (uint256 mintPrice, uint256 burnPrice);
```

Both **transactable** share prices in one read, computed off a shared bucket scan: `mintPrice` is what `supplyCollateral` mints at (accrued-only conservative upper bound, **not** vesting-deducted), `burnPrice` is what `withdrawCollateral` burns at (== `getPoolBurnPrice`, before the per-LP profit-vest cap — `getLpValue` applies that). Returns `(1e18, 1e18)` for an empty pool. Values are still returned in states where the transactions themselves would revert (underwater floor `E414`, expired-unsettled `E415`) — gate on those separately, e.g. via [`previewSupply`](#previewsupply) / [`viewMarketPoolState`](#viewmarketpoolstate). Reverts `E300` if the market doesn't exist.

### vestingOutstanding

```solidity
function vestingOutstanding(bytes32 marketId) external view returns (uint256);
```

The market's still-withheld settlement vesting (swap token decimals), decaying linearly to zero at its drip horizon. It is subtracted from every burn/NAV read while nonzero; the tokens themselves stay in `pool.totalCollateral` and outstanding LP shares keep their pro-rata claim. Integrators valuing a *held* LP position add `shares × vestingOutstanding / totalShares` back to the deducted mark (that's what [`lpVestingClaim`](#lpvestingclaim) computes). Reverts `E300`.

### anyBucketCapBinds

```solidity
function anyBucketCapBinds(bytes32 marketId) external view returns (bool);
```

`true` iff any occupied bucket has a **buyer-side collateral cap binding** — i.e. a liquidatable buyer whose liquidation would seize collateral into the pool. A non-maskable pending-recovery signal: vault-style integrators use it to gate permissionless deallocation until the liquidation crystallizes. Reverts `E300`.

***

## SwapCore — oracle index reads

### updateMarketRateIndex

```solidity
function updateMarketRateIndex(bytes32 marketId) external;
```

**Who calls:** anyone — typically a keeper, but there's no gate.

**What it does:** first best-effort pokes any of the market's oracles (`referenceRateOracle`, `baseSwapRateOracle`, `riskPremiumOracle`) that advertised `IPokeable` at market creation — refreshing their internal state (e.g. TWAR buffers) so the index snapshot reflects the freshest values. Pokes are **rate-limited per oracle** by `admin.pokeMinSpacingSec()` (default **3600 seconds**, timelocked, hard floor of 60 seconds): a poke attempted inside the spacing window is skipped entirely, and a same-timestamp re-entry is short-circuited. Pokes are best-effort — a failing poke never reverts the call, and its window is rolled back so the next caller can retry. It then calls `rateIndex.update(market.referenceRateOracle)`, writing a fresh cumulative index entry keyed by `block.timestamp`. Used to:

* Force an index snapshot at a specific timestamp (useful when a market has been quiet for a while and a keeper wants to refresh before heavy read activity, and **required** post-maturity for settlement inside the grace window).
* Pre-update the index immediately before batch settlement so `makePayment` doesn't re-read the oracle for every swap in the batch — though `makePayment` already does this per-call, so it's rarely necessary.

Under normal operation every state-changing function already updates the index, so you typically don't need to call `updateMarketRateIndex` directly.

**Reverts:**

| Code   | Reason                                          |
| ------ | ----------------------------------------------- |
| `E300` | Market doesn't exist.                           |
| `E606` | Oracle returned invalid data during the update. |

**See also:** [`getFreshSwapNetAmount`](#getfreshswapnetamount), [`getPoolSharePrice`](/protocol/liquidity.md#getpoolshareprice).

### getOracleIndexData

```solidity
function getOracleIndexData(address oracle)
    external view returns (uint256 currentIndex, uint256 lastUpdate,
                           Types.RateConvention convention, int256 cumulativeRateTime);
```

Stored index state for an oracle **address** (not a market ID — index history is global per oracle and shared by every market using it): last recorded index (RAY), timestamp of the last write, the immutable rate convention, and the cumulative rate-time accumulator (spot conventions only). An oracle that was never initialized returns zeros. This is the raw storage read; for a projected-to-now value use [`getFreshIndex`](#getfreshindex).

### getFreshIndex

```solidity
function getFreshIndex(bytes32 marketId) external view returns (uint256);
```

Projected-forward oracle index for view-only paths — no state mutation. For `Cumulative` oracles it reads the adapter live (clamped at the stored floor, mirroring the monotonic clamp `rateIndex.update` applies); for spot conventions it projects from the stored rate-time accumulator plus the current spot rate. Reverts `E606` on an invalid oracle response and `E725` if rate/decimals normalization is out of bounds. No market-exists guard — an unknown `marketId` resolves to a zero oracle address and reverts raw.

### getIndexAt

```solidity
function getIndexAt(bytes32 marketId, uint256 targetTime) external view returns (uint256);
```

Resolves the **historical** index of the market's reference oracle at `targetTime` from stored snapshots: interpolated between bracketing snapshots, extrapolated from the last stored pair beyond them, and returned flat at the oldest value for times before the first snapshot. Pure storage read — no oracle call, so the answer only improves as keepers snapshot more densely (see [`updateMarketRateIndex`](#updatemarketrateindex)).

***

## SwapCore — queue & introspection

### getExpiryQueueState

```solidity
function getExpiryQueueState(bytes32 marketId)
    external view returns (uint256 length, uint256 pointer);
```

The market's expiry queue length and current pointer in one call. The queue is append-only (one entry per `buySwap`, in entry order); `pointer` is the source of truth for "earliest possibly-unsettled entry" — everything before it is settled. Returns `(0, 0)` for unknown markets.

### getExpiryQueueEntry

```solidity
function getExpiryQueueEntry(bytes32 marketId, uint256 index) external view returns (bytes32);
```

The swap ID at `index` in the expiry queue. `bytes32(0)` means a settled entry that has been zeroed (the array never shrinks). Reverts with an array out-of-bounds panic for `index >= length`. Keepers walk `[pointer, length)` with this to find settleable swaps.

### isLocked

```solidity
function isLocked() external view returns (bool);
```

`true` while SwapCore is executing under its reentrancy guard — i.e. any state-changing entry point (settlement, liquidation, buys, LP ops) is mid-flight. Exists so contracts receiving tokens *during* a SwapCore call (e.g. `SwapPositionWrapper` inside a settlement-token callback) can detect that `settlementPayouts` / `escrowedCollateral` are not yet final and defer crediting to a later call. Always `false` when read from a top-level `eth_call`.

### admin

```solidity
Admin public immutable admin;
// getter: function admin() external view returns (address)
```

The protocol `Admin` contract (fee config, multisig, timelocked parameters such as `pokeMinSpacingSec` and the settlement grace period). Bound at construction and never reassigned — repointing would require a new SwapCore deployment.

***

## State-variable getters

SwapCore exposes several public mappings and arrays. Solidity auto-generates a getter for each; these are the ones integrators most commonly read.

#### markets

```solidity
mapping(bytes32 => Types.Market) public markets;
// getter: function markets(bytes32) external view returns (...)
```

Returns the full `Types.Market` struct. `Market` has no mappings or dynamic arrays, so the auto-getter returns every field (22 in total), including the nested `Pool` sub-struct (`totalShares`, `totalCollateral`, `lockedCollateral`). Use this for market-config reads: oracles, pool (shares/collateral/locked), fee-curve parameters, `earlyExitAllowed`, `earlyExitFee`, `liquidationIncentive`, `terminated`, `marketOwner`, `lpWhitelistEnabled`, `minCollateral`, `bucketInterval`, `riskPremiumOracle`, `numBuckets`.

Non-existent markets return the zero-initialized struct with `exists = false`. Check `.exists` before treating the response as live data.

#### swapPositions

```solidity
mapping(bytes32 => Types.SwapPosition) public swapPositions;
// getter: function swapPositions(bytes32) external view returns (...)
```

Returns the 14-field `Types.SwapPosition` tuple:

```
(marketId, userAddress, settled, isEarlyExit, collateralBalance,
 notionalAmount, baseRate, swapRate, entryTimestamp, entryFloatingIndex,
 poolCollateralBacking, utilFee, riskPremium, liquidationBounty)
```

A `swapId` that doesn't exist returns all zeros — check `entryTimestamp != 0` to confirm the swap is real. (`Views.viewSwap` returns the same data as a named struct.)

#### settlementPayouts

```solidity
mapping(bytes32 => uint256) public settlementPayouts;
```

After settlement, records the amount **actually transferred to the buyer** for `swapId` (`buyerCollateralReleased`). If the buyer-side transfer **fails** and the payout is escrowed instead, this slot is set to `0` and the amount is recorded in [`escrowedCollateral`](#escrowedcollateral) — the two are mutually exclusive, never double-counted. So `0` means one of: the swap isn't settled yet, the buyer was due nothing (lost the P\&L / was liquidated), **or** the payout failed to transfer and is now sitting in escrow.

For the buyer's **remaining on-core claim**, prefer [`getBuyerSettlementValue`](#getbuyersettlementvalue) — on a settled swap it returns exactly the escrowed remainder in one call (a successfully transferred payout has already left the protocol). If you specifically want the *historical credited amount* including what was already paid out, read `settlementPayouts[swapId] + escrowedCollateral[swapId]` — the sum `exitSwapEarly` uses for its slippage check and the pattern `SwapPositionWrapper` follows. Neither read is a direction flag: a losing buyer can still have a nonzero payout, so you can't infer `netRecipient` from it.

#### escrowedCollateral

```solidity
mapping(bytes32 => uint256) public escrowedCollateral;
```

Non-zero when a buyer-side settlement or liquidation transfer failed (usually because the recipient became unable to receive the token). The balance is held until the buyer (or an authorized delegate) calls [`claimEscrow`](/protocol/swaps.md#claimescrow). Funds are earmarked to the **original** `swap.userAddress` — no one can redirect them. If the recipient is still blocked, the `claimEscrow` transfer reverts and the escrow stays in place.

#### isAuthorized

```solidity
mapping(address => mapping(address => bool)) public isAuthorized;
```

See [Authorization](/protocol/authorization.md#isauthorized). Returns `true` only for explicitly-set delegations; the implicit self-path (`msg.sender == onBehalfOf`) is **not** stored in this mapping, so code that mirrors SwapCore's auth rule should check `operator == owner || isAuthorized[owner][operator]`.

#### marketLpWhitelist

```solidity
mapping(bytes32 => mapping(address => bool)) public marketLpWhitelist;
```

Returns whether an address is whitelisted to supply LP collateral in a specific market. Only meaningful when `markets[marketId].lpWhitelistEnabled == true`. The market creator is auto-whitelisted at `createMarket` time **only if `lpWhitelistEnabled` was true at creation** — enabling the whitelist later does not retroactively whitelist the creator.

#### pendingMarketOwner

```solidity
mapping(bytes32 => address) public pendingMarketOwner;
```

Address that has been designated as the new owner of `marketId` but has not yet accepted via [`acceptMarketOwnership`](/protocol/market-admin.md#acceptmarketownership). `address(0)` if no transfer is in flight.

#### allMarketIds (array)

```solidity
bytes32[] public allMarketIds;
// getter: function allMarketIds(uint256 index) external view returns (bytes32)
```

Positional read by index. Use [`getAllMarketIds()`](/protocol/market-admin.md#getallmarketids) to fetch the whole array at once, or [`viewAllMarkets`](#viewallmarkets) for paginated IDs + full market data.

#### Bucket aggregates are not readable on-chain

The per-market bucket aggregates (`Types.Bucket`) that back the share-price calculation live in an **`internal`** mapping — there is **no auto-generated getter** on SwapCore, and no lens function exposes them. No protocol path needs the tuple; tests read buckets through a dedicated harness.

For reference (e.g. offline analysts decoding raw storage), the current `Types.Bucket` struct has **32 fields** in this declaration order:

```
( 1) lpNotional            uint256    (17) minEntryIndex         uint112
( 2) weightedLpRate        int256     (18) maxEntryIndex         uint112
( 3) weightedEntryTime     uint256    (19) memberCount           uint32
( 4) weightedInverseIndex  uint256    (20) minFeeSum             uint128
( 5) weightedEntryIndex    uint256    (21) maxFeeSum             uint128
( 6) weightedLnIndex       int256     (22) minEntryTime          uint40
( 7) weightedUtilFee       uint256    (23) maxEntryTime          uint40
( 8) weightedRiskPremium   uint256    (24) weightedLpRateSq      uint256
( 9) totalBuyerCollateral  uint256    (25) weightedIndexSq       uint256
(10) totalPoolBacking      uint256    (26) minMemberNotional     uint256
(11) weightedFeeTime       uint256    (27) sumNotionalSq         uint256
(12) weightedRateSq        uint256    (28) weightedFeeSq         uint256
(13) weightedRateTime      int256     (29) minCapSurplus         uint128
(14) weightedTimeSq        uint256    (30) maxCapSurplus         uint128
(15) minLpRate             int128     (31) weightedCapSurplus    uint256
(16) maxLpRate             int128     (32) weightedCapSurplusSq  uint256
```

Notes for anyone rebuilding valuation from these aggregates:

* `weightedLnIndex` (6) is populated for **`SpotCompoundRate` markets only** and is zero on other conventions. It is signed — a zero is a legitimate value, not an absence — so key netting eligibility on `weightedInverseIndex` instead.
* Fields 15–23 are the **value boxes**: per-member parameter ranges that bracket every active member, consumed by `BucketBoundsLib` for conservative NAV bounds. `minFeeSum`/`maxFeeSum` (20–21) are a single **merged** fee axis — min/max over members of `utilFee + riskPremium` — since every valuation consumes the pair only through its sum. Boxes are ranges, not sums; don't aggregate them like the weighted moments.
* Fields 24–28 are certified-tightening second moments (they let removal re-certify the boxes from survivor aggregates), and 29–32 are the **cap-surplus** box/moments tracking each member's paired-collateral sizing headroom.

`Types.Bucket` in `interfaces/Types.sol` is the source of truth; treat the list above as a convenience snapshot.

This is mostly of interest to offline analysts rebuilding the share-price calculation. For ordinary consumers, [`getPoolBurnPrice`](/protocol/liquidity.md#getpoolburnprice) is the right read for redeemable value and [`getPoolSharePrice`](/protocol/liquidity.md#getpoolshareprice) for an informational mark — see [The three share prices](/protocol/liquidity.md#the-three-share-prices).

***

## The Views lens contract

`lib/Views.sol` is a standalone read-only contract deployed at its own address, holding a single immutable pointer to SwapCore (readable via `core()`). It batches multi-market reads, previews LP operations with exact parity to the live paths, and normalizes oracle reads for UIs. **Call everything below against the `Views` address.**

Two conventions to know:

* Batched functions (`viewLPPositions`, `viewLPDashboard`, …) silently **skip** non-existent markets and zero-share positions rather than reverting.
* Single-market functions guard existence inconsistently: the preview functions revert `E300`, while `getBaseRate` / `calculateSwapRate` / `previewUtilizationFee` / `viewMarketOracleIndex` revert with the **string** `"Market does not exist"`. Match on both if you need robust error handling.

### Swap & market reads

#### viewSwap

```solidity
function viewSwap(bytes32 swapId) external view returns (Types.SwapPosition memory swap);
```

The full `SwapPosition` as a named struct — same data as SwapCore's positional `swapPositions` getter, friendlier ABI. Non-existent swaps return a zeroed struct; check `entryTimestamp != 0`.

#### getSwapTerm

```solidity
function getSwapTerm(bytes32 marketId) external view returns (uint32 swapTerm);
```

Just the market's swap term in seconds (gas-optimized single-field read). Returns `0` for unknown markets.

#### viewAllMarkets

```solidity
function viewAllMarkets(uint256 offset, uint256 limit, bool onlyActive)
    external view returns (Types.Market[] memory markets, bytes32[] memory marketIds, uint256 total);
```

Paginated market listing; `onlyActive = true` filters out terminated markets. `marketIds` corresponds 1:1 with `markets`, and `total` is the count matching the filter (use it to drive pagination). An `offset` past the end returns empty arrays plus `total`.

#### viewMarkets

```solidity
function viewMarkets(bytes32[] calldata marketIds) external view returns (Types.Market[] memory);
```

Full `Market` structs for a specific set of IDs, in order. Non-existent IDs yield zeroed structs (`exists == false`).

#### viewTotalLPCollateral

```solidity
function viewTotalLPCollateral(bytes32[] calldata marketIds) external view returns (uint256);
```

Sums `pool.totalCollateral` across the given markets — but **only markets whose `swapToken` is Base-mainnet USDC** (hardcoded filter); other tokens contribute zero. A TVL convenience for the USDC frontend, not a general aggregator.

#### viewMarketPoolState

```solidity
function viewMarketPoolState(bytes32 marketId)
    external view returns (MarketPoolState memory s);
```

Single-call market-page payload: `mintPrice`, `burnPrice` (pre-cap), `fairPrice`, `vestingOutstanding`, `totalShares` / `totalCollateral` / `lockedCollateral` / `availableCollateral`, `utilizationWad`, plus two gate flags — `capBinds` ([`anyBucketCapBinds`](#anybucketcapbinds)) and `lpOpsBlocked` (`true` when supply/withdraw would revert `E415` on expired-unsettled swaps). Prices are still returned in states where the transactions themselves revert — gate on `lpOpsBlocked` / [`previewSupply`](#previewsupply). Reverts `E300`.

### Pricing & quote previews

#### getBaseRate

```solidity
function getBaseRate(bytes32 marketId) external view returns (int256);
```

The market's current base rate (WAD, signed) from its `baseSwapRateOracle` at the market's full `swapTerm` tenor — validated and normalized exactly as `buySwap` reads it. Reverts on unknown markets (string) and on an unhealthy oracle (`E611`).

#### calculateSwapRate

```solidity
function calculateSwapRate(bytes32 marketId, uint256 notionalAmount) external view returns (int256);
```

Preview of the all-in swap rate `buySwap` would produce for `notionalAmount` right now: base rate + seasoned utilization fee + risk premium for BUY\_FIXED. For BUY\_FLOATING it previews the instantaneous effective rate on spot-convention markets and returns **`0` on Cumulative markets** — matching the stored `swap.swapRate == 0` sentinel, since the floating leg is derived from index growth at settlement. **Display only**; SwapCore never stores the BUY\_FLOATING preview.

#### previewUtilizationFee

```solidity
function previewUtilizationFee(bytes32 marketId, uint256 notionalAmount)
    external view returns (uint256 utilizationFee);
```

The exact utilization fee rate (WAD) `buySwap` would charge for `notionalAmount`, priced against **seasoned (EMA) liquidity** exactly like the live path. Use this instead of reimplementing the curve: the fee depends on a stored EMA accumulator plus `block.timestamp` and is not reproducible off-chain from the fee-curve parameters alone.

#### getCalculatedAvailableLiquidity

```solidity
function getCalculatedAvailableLiquidity(bytes32 marketId) external view returns (uint256);
```

Returns the **maximum notional** a new swap can use right now, given the pool's unlocked collateral and the current base rate. Computed as:

```
availableLiquidity = SwapFormulas.calculateAvailableLiquidity(
    totalLpAvailableCollateral(marketId),
    baseRate,
    swapTerm,
    leverageMultiplier
)
```

In effect: "how much notional can this pool safely back, given that each notional unit consumes `|baseRate| × swapTerm × leverageMultiplier` worth of collateral for worst-case LP exposure?"

Use this to size the `notionalAmount` you pass to `buySwap`. If `notionalAmount > getCalculatedAvailableLiquidity(marketId)` at tx execution time, `buySwap` reverts. Because base rates move, quote-time and execute-time values can differ slightly — if that matters, pair it with `buySwap`'s `rateBound` slippage guard.

**Reverts:** can revert with `E611` / oracle-related errors if the base rate oracle is unhealthy.

**See also:** [`totalLPAvailableCollateral`](/protocol/liquidity.md#totallpavailablecollateral), [`buySwap`](/protocol/swaps.md#buyswap).

### LP previews & dashboards

#### previewSupply

```solidity
function previewSupply(bytes32 marketId, uint256 collateralAmount)
    external view returns (uint256 sharesOut, uint256 mintPrice);
```

Exact shares `supplyCollateral` would mint for `collateralAmount` this block, replicating its rounding and amount-math guards — it reverts with the **same errors** the live call would: `E300` (no market), `E400` (zero amount / dust deposit), `E414` (underwater floor with active swaps), `E415` (expired-unsettled swaps), `E728` (index at floor). Caller-shaped guards are deliberately **not** replicated (authorization `E206`, LP whitelist `E201`). Parity holds when quote and transaction land in the same block/oracle state; across blocks they can diverge — that's what `minSharesOut` is for.

#### previewWithdraw

```solidity
function previewWithdraw(bytes32 marketId, address lpAddress, uint256 amount)
    external view returns (uint256 sharesBurned, uint256 amountOut, bool capApplied, uint256 effectivePrice);
```

Exact outcome `withdrawCollateral` would produce this block, replicating the profit-vest cap, the full/partial rounding split, and every last-LP branch. Pass `type(uint256).max` (or anything ≥ the position's full value) for a full exit. Reverts with the same errors as the live call: `E300`, `E400`, `E405` (insufficient available collateral), `E407`, `E413` (same-block deposit), `E415`, `E416`, `E417` (capped full last-LP exit), `E419` (undripped vesting on full last-LP exit). Caller slippage params (`minCollateralOut`, `maxSharesToRedeem`) and auth/receiver checks are not replicated — feed this preview's outputs into those params instead. `effectivePrice` is the price actually applied: `min(getPoolBurnPrice, entryPrice)`.

#### getLpVestState

```solidity
function getLpVestState(bytes32 marketId, address lpAddress)
    external view returns (uint256 entryPrice, uint64 vestEndTimestamp, bool capActive);
```

**Who calls:** anyone — primarily frontends that want to warn an LP about the profit-vest cap before they supply or withdraw.

**What it does:** reports the LP's current profit-vest state for a market. Reads the LP's position via `SwapCore.getLpPosition`, then returns the vest anchor, the window end, and whether the cap is currently binding:

* `entryPrice` — the profit-vest anchor (share price recorded at the deposit that began the current vest window), WAD.
* `vestEndTimestamp` — unix seconds; the cap applies while `block.timestamp < vestEndTimestamp`.
* `capActive` — `true` iff `block.timestamp < vestEndTimestamp && getPoolBurnPrice(marketId) > entryPrice`. When `true`, a `withdrawCollateral` right now would burn shares at `entryPrice` rather than the live (higher) price.

  The comparison is against [`getPoolBurnPrice`](/protocol/liquidity.md#getpoolburnprice) — the mark `withdrawCollateral` actually burns at. Mirror that price if you reimplement the check; the fair mark sits above it and would report the cap as binding across a band where it does not.

For a fully-exited position (`shares == 0`), returns `(0, 0, false)` — stale anchor storage is ignored. See [Liquidity provision → Profit-vest cap](/protocol/liquidity.md#how-the-pool-works) for the full mechanism.

**Reverts:** `E300` if the market doesn't exist (propagated from `SwapCore.getLpPosition`).

**See also:** [`getLpValue`](/protocol/liquidity.md#getlpvalue) (applies the cap to return the true redeemable amount), [`withdrawCollateral`](/protocol/liquidity.md#withdrawcollateral).

#### lpVestingClaim

```solidity
function lpVestingClaim(bytes32 marketId, address lpAddress) public view returns (uint256);
```

The LP's pro-rata claim (`shares / totalShares`) on the market's still-withheld settlement vesting ([`vestingOutstanding`](#vestingoutstanding)), in swap token decimals. Display/valuation data, **not** a separately claimable balance — the value drips back into the share price on its own. Rounding mirrors the on-chain deduction (floor twice), so `burn value + claim` never exceeds the undeducted book value. Returns `0` (rather than reverting) for unknown markets and share-less LPs.

#### viewLPDashboard

```solidity
function viewLPDashboard(address lpAddress, bytes32[] calldata marketIds)
    external view returns (LpDashboardEntry[] memory entries);
```

Everything an LP position row needs, one entry per market where the LP has shares: the `LpPosition`, `burnValue` (full-exit value, per-LP cap applied), `fairValue` (informational, non-transactable), `mintPrice` / `burnPrice` (pre-cap) / `fairPrice`, `capActive`, `vestingClaim`, pool-level `vestingOutstanding`, the full `Market`, and the reference oracle's `description()` (empty string when it reverts). Successor to `viewLPPositions` — prefer it in new integrations. Skips non-existent markets and zero-share positions.

#### viewLPPositions

```solidity
function viewLPPositions(address lpAddress, bytes32[] calldata marketIds)
    external view returns (bytes32[] memory, Types.LpPosition[] memory, uint256[] memory values,
                           uint256[] memory sharePrices, Types.Market[] memory, string[] memory descriptions);
```

Legacy batched LP read: positions, `getLpValue` values, **fair** share prices, market structs, and oracle descriptions for every given market where the LP holds shares. Predates the mint/burn price separation and settlement vesting — it reports only the informational fair midpoint. Use [`viewLPDashboard`](#viewlpdashboard) instead.

#### viewLPPoolSharesByAddress

```solidity
function viewLPPoolSharesByAddress(address user, bytes32[] calldata marketIds)
    external view returns (bytes32[] memory, uint256[] memory shares, uint256[] memory sharePrices);
```

Lightweight filter: for each given market where `user` holds shares, returns the market ID, share balance, and the **fair** (informational) share price. Same fair-midpoint caveat as `viewLPPositions`.

### Oracle index reads

#### viewMarketOracleIndex

```solidity
function viewMarketOracleIndex(bytes32 marketId) external view returns (OracleIndexInfo memory);
```

[`getOracleIndexData`](#getoracleindexdata) for the market's reference oracle, wrapped in a struct that includes the oracle address: `{oracle, currentIndex, lastUpdate, convention, cumulativeRateTime}`. Reverts on unknown markets (string).

#### viewOracleIndex

```solidity
function viewOracleIndex(address oracle) external view returns (OracleIndexInfo memory);
```

Same payload, keyed directly by oracle address. Never-initialized oracles return zeros.

#### viewAllOracleIndexes

```solidity
function viewAllOracleIndexes(uint256 offset, uint256 limit)
    external view returns (OracleIndexInfo[] memory infos, uint256 total);
```

Index data for every **unique** reference rate oracle found in a page of the market list. Pagination is over markets, not oracles — `total` is the market count, and a page can return fewer infos than markets scanned (duplicates and non-existent markets are skipped).

#### viewCurrentReferenceRate

```solidity
function viewCurrentReferenceRate(bytes32 marketId, uint256 windowSeconds)
    external view returns (int256 rateWad, bool isValid);
```

The market's "current" reference rate, normalized to WAD. Spot conventions return the live `getRate()` reading (`windowSeconds` ignored); Cumulative markets derive a continuously-compounded annualized rate from index growth over the trailing `windowSeconds` (using stored snapshots — shorter windows need denser snapshots, i.e. someone calling `updateMarketRateIndex` regularly). `isValid` is `false` for unknown markets, failed/invalid spot reads, a zero or over-long window, or a non-increasing index.

Caveats: `isValid` is **not a staleness gate** — past the last snapshot the index is extrapolated, so a stale-but-rising history still reads valid; a window whose start predates the first snapshot is annualized over data that doesn't cover it; and a spot oracle whose `decimals()` drives normalization out of range reverts the view outright rather than returning `(0, false)`. Callers needing freshness should check [`viewMarketOracleIndex`](#viewmarketoracleindex)`.lastUpdate` themselves.

### Base-rate oracle governance aids

#### maxRequiredTenorForBaseRateOracle

```solidity
function maxRequiredTenorForBaseRateOracle(address oracle) external view returns (uint256 maxTenor);
```

The longest base-rate tenor that markets still backed by `oracle` require it to serve — a pre-flight check before shortening a timelocked base-rate oracle's tenor curve (shortening below this value makes the oracle fail closed for affected tenors and reverts every base-rate read for those markets with `E611`, freezing LP pricing/supply/withdraw). A market "constrains" the oracle while it can take new buys (`!terminated`) or still has locked exposure; fully wound-down terminated markets don't. `oracle` is matched against the address stored on the market (pass the wrapper address for wrapped deployments). Returns `0` if nothing constrains it. O(number of markets), O(1) memory.

#### requiredTenorsForBaseRateOracle

```solidity
function requiredTenorsForBaseRateOracle(address oracle) external view returns (uint256[] memory tenors);
function requiredTenorsForBaseRateOracle(address oracle, uint256 offset, uint256 limit)
    external view returns (uint256[] memory tenors, uint256 nextOffset);
```

Diagnostic companion: the per-market `swapTerm`s that still constrain `oracle` (a market pair contributes one entry per side), under the same matching rules as above. The paginated overload pages over the raw market-ID index space — a window may return fewer entries than it scanned, or none; `limit = 0` scans to the end, and `nextOffset == allMarketIds.length` signals completion.

### core

```solidity
ISwapCore public immutable core;
// getter: function core() external view returns (address)
```

The SwapCore address this lens reads from. Fixed at deployment — useful for verifying you're pointed at the right protocol instance.


---

# 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/views.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.
