> 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

Read-only paths into SwapCore, plus the one helper that exists only to refresh the oracle index for consumers who want a guaranteed-fresh read. Everything on this page is safe for off-chain consumers (frontends, indexers) to call via `staticCall`, with the one documented exception.

Source: `SwapCore.sol` (most entries) and `lib/Views.sol` (the `getCalculatedAvailableLiquidity` helper).

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

***

## 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 runs the same fixed/floating payment math that `makePayment` uses at actual 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:** use the snapshot written at `expiryTimestamp` via `rateIndex.getIndexAt`; if no snapshot exists yet, fall back to `getIndexAtVirtual` — which projects a fresh index at `block.timestamp` and interpolates it back to `expiryTimestamp` (matching what settlement would resolve, so a stale view can't mask a pending settlement).
4. Derive `floatingPaymentRate` from `(entryFloatingIndex, effectiveIndex, rateDuration)` using `SwapFormulas.deriveRateFromIndex`.
5. Compute `fixedPayment` and `floatingPayment` via `SwapFormulas.calculateSwapPayment`. For **BUY\_FLOATING**, `utilFee` and `riskPremium` are added to the floating leg; for **BUY\_FIXED**, fees are already baked into `swap.swapRate`.
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`.                            |

**Notes:**

* 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. 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 total payout** → `settlementPayouts[swapId]` (plus `escrowedCollateral[swapId]` if the buyer-side transfer failed). Note this is the buyer's *credited amount* — returned collateral plus any winnings, or `0` if the buyer lost everything or was liquidated — **not** the net obligation and **not** a direction flag. A losing buyer can still have `settlementPayouts > 0`, so you can't infer `netRecipient` from it.

**See also:** [`getFreshSwapNetAmount`](#getfreshswapnetamount), [`settlementPayouts`](#settlementpayouts), 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).

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

***

## getCalculatedAvailableLiquidity

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

**Deployed on the `Views` helper contract, not SwapCore.** Call it against the `Views` address.

**Who calls:** anyone.

**What it does:** 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 via `Utils.validateSwapParams`. 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).

***

## getLpVestState

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

**Deployed on the `Views` helper contract, not SwapCore.** Call it against the `Views` address.

**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 && getPoolSharePrice(marketId) > entryPrice`. When `true`, a `withdrawCollateral` right now would burn shares at `entryPrice` rather than the live (higher) price.

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).

***

## 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 de-duplicated per oracle per block and are best-effort (they don't revert the call). 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).
* 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).

***

## 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, 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.

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

Because of that last case, integrators should read post-settlement entitlement as **`settlementPayouts[swapId] + escrowedCollateral[swapId]`**, not `settlementPayouts` alone — exactly the sum `exitSwapEarly` uses for its slippage check, and the pattern `SwapPositionWrapper` follows.

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

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

### buckets

```solidity
mapping(bytes32 => mapping(uint256 => Types.Bucket)) public buckets;
// getter: function buckets(bytes32 marketId, uint256 bucketId) external view returns (...)
```

Aggregated swap data for `(marketId, bucketId)`:

```
(lpNotional, weightedLpRate, weightedEntryTime, weightedInverseIndex,
 weightedUtilFee, weightedRiskPremium, totalBuyerCollateral, totalPoolBacking,
 weightedFeeTime)
```

This is mostly of interest to offline analysts rebuilding the share-price calculation. For ordinary consumers, [`getPoolSharePrice`](/protocol/liquidity.md#getpoolshareprice) is the right read.


---

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