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

## 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`, falling back to the fresh projected index if no snapshot exists (prevents a stale view from masking settlements).
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".
* After `swap.settled == true`, this function returns `(0, <buyerOwes>)` (the internal branch that would compute a net only runs when `!settled`), so it is not a way to retrieve historical settlement amounts. Use `settlementPayouts[swapId]` for that.

**See also:** [`getFreshSwapNetAmount`](#getfreshswapnetamount), [`settlementPayouts`](#settlementpayouts).

***

## 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);
```

**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 `E606` / oracle-related errors if the base rate oracle is unhealthy.

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

***

## updateMarketRateIndex

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

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

**What it does:** looks up `market.referenceRateOracle`, then calls `rateIndex.update(oracle)`, 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 (minus the nested dynamic arrays, which are not included by the auto-getter). 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 13-field `Types.SwapPosition` tuple:

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

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 transferred **to the buyer's side** for `swapId` (the amount credited via `safeTransfer` or moved to `escrowedCollateral` if the direct transfer failed). `0` means either the swap hasn't been settled yet or the buyer received nothing at settlement (e.g., lost the P\&L and was not due a residual).

Paired with [`escrowedCollateral`](#escrowedcollateral), this is how integrators read post-settlement entitlement without needing balance-diff accounting. `SwapPositionWrapper` uses exactly this pattern.

### 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)
```

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: 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:

```
GET https://docs.kairosswap.com/protocol/views.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
