---
title: "Providing liquidity"
description: "Single-sided deposit into one pool leg, the per-leg bLP receipt and its liquidity index, the pool-level solvency rate C every mint and exit settles at, the anti-JIT cooldown, and what a same-asset or cross-asset exit pays"
audience: both
type: guide
status: live
lang: en
publish: true
aliases: [4-2-providing-liquidity]
---
# Providing liquidity

A deposit is **single-sided**: you send one listed token and receive an ERC-20 receipt for that leg
only, never a proportional basket share. Denomination, price exposure and fee yield stay per leg.
Solvency does not: every mint and every exit settles at one pool-level rate $C$, so a real loss on
any leg is shared pro-rata by every [LP](/docs/glossary#liquidity-provider-lp) in the pool, and so
is every surplus. Covers the app surface (`/pools` → Manage) and the contract calls behind it.
[Coverage ratio](/docs/glossary#coverage-ratio) and the pool rate are derived in
[Inventory Management §5](/docs/1-1-1-inventory-management#5-lp-settlement-at-the-pool-rate);
what your liquidity charges a swap is in [Spread & Fees](/docs/1-1-4-spread-fees).

## 1. What a position is

Each `(pool, listed asset)` pair has exactly one [`LPToken`](/docs/glossary#lp-token), an EIP-1167 clone-with-immutable-args deployed at `initAsset` (`PoolConfig._deployLpToken`). Its on-chain symbol is `bLP-<SYM>` and its name `BTR LP: <SYM>`, both read off the underlying token; the app displays the same receipt as `<SYM>.<core acronym>` (e.g. `USDT.sc`). The pool address is pinned in the clone's code and is the sole mint and burn authority.

A receipt balance is not 1:1 with the underlying. The claim is

$$\text{underlying} = \frac{\text{balance} \cdot \text{liquidityIndexWad}}{10^{18}}$$

That underlying amount is **face**, denominated in your token. Your price exposure is that one token: a depeg of it lands on its own leg's LPs. What face redeems for is face times the pool rate $C$ (§5), and $C$ moves with every leg in the pool.

Your leg's coverage $c = R/L$ is moved by the whole core: a swap that delivers your asset raises it, a swap that takes your asset lowers it. Cores are grouped by correlated asset class so that the two tend to offset. A drained leg against a filled one is inventory location, not loss, and $C$ nets it to nothing. Coverage reaches you in one place: it caps what a same-asset exit can deliver in your own token (§5.1). Why a core holds what it holds: [Pool Composition](/docs/2-3-pool-composition#why-several-cores-rather-than-one).

The [`liquidityIndexWad`](/docs/glossary#liquidity-index) starts at `LIQUIDITY_INDEX_INIT_WAD` = $10^{18}$ (`PoolConstantsLib.sol`) and rises when LP fees accrue to the leg (`PoolLiquidity.accrueLpFee`, `raiseIndex`). It is a `uint96`: `raiseIndex` clamps at `type(uint96).max` rather than reverting, and value above the clamp stays in [`liabilities`](/docs/glossary#liabilities) unclaimable. `hookWriteDown` floors the leg's book at the smallest liability the index can still represent, so the index stays at or above 1 through any loss, a total loss included, and the leg stays live (`test_total_loss_leaves_the_leg_live_and_depositable`). `mintIndex` still reverts `InvalidState` on an index of 0, as a fail-closed guard.

## 2. Deposit

`Pool.deposit(address token, uint256 amount)` is `payable` and forwards to `PoolLiquidity.deposit`. Shares always mint to `msg.sender`: the entrypoint takes no recipient, no `minLpOut` and no deadline.

1. **Approve.** The app reads `allowance(user, pool)` and, if short, prepends `approve(pool, amount)`, the exact amount by default, `MAX_UINT256` only if you opt into infinite approval. Both calls go out as one EIP-5792 batch where the wallet supports `sendCalls`, otherwise sequentially.
2. **Validate.** `amount == 0` reverts `ZeroValue`. `PoolIOLib.asset` resolves the leg, mapping the NATIVE sentinel to wnative, and reverts `NotFound` on an unlisted token. `checkRiskFlags` reverts `FeatureDisabled` if `HALT_RISK_BIT` or `HALT_GUARDIAN_BIT` is set on the leg. A leg carrying `DEPOSIT_GATED_BIT` (bit 8) reverts `NotAuth` unless `AccessControl.isDepositor(msg.sender)`: the GEN-4 guarded-launch allowlist, which gates `deposit` and `donate` only, never an exit. A leg with `reserves == 0 && liabilities != 0` reverts `InvalidState`.
3. **Rate.** `PoolSolvency.solvency` computes $C$ and the call reverts `FeedUnavailable` if any armed leg's mark fails `FeedMathLib.gate`: a deposit priced off a pool whose value is unknown refuses rather than degrades. On an armed pool `PoolIOLib.priceBandGuard` also checks this leg's [depeg band](/docs/glossary#depeg-band).
4. **Pull.** `PoolIOLib.pull` calls `requireNoFlash` ([flash](/docs/glossary#flash-loan) callbacks cannot deposit, closing the repay-by-depositing double credit), wraps `msg.value` via `IWETH9.deposit` for the NATIVE sentinel and refunds the excess, otherwise rejects nonzero `msg.value` and does a balance-delta `safeTransferFrom`. A fee-on-transfer token therefore credits only what actually arrived.
5. **Mint at C.** `face = amt * 1e18 / C`, floored toward the pool; `idx = asset.liquidityIndexWad` (`mintIndex` reverts `InvalidState` at 0); `lpAmt = face * 1e18 / idx`, less the dead-share seed on a first credit ([§3](#3-the-dead-share-seed)). `face == 0` or `lpAmt == 0` reverts `ZeroValue`, so a dust deposit that would mint nothing cannot be gifted to existing LPs.
6. **Book.** `reserves += amt` and `liabilities += face`, then `ILPToken.mint`. Two soft caps are then checked on the post-mint book: `requireWeightOk` reverts `WeightCapExceeded` if a non-zero `maxLiabWeightBps` is passed, and `requireDepositCap` reverts `ExcessiveAmount` if the leg's notional in base tokens passes `depositCapCode` (`m·10^e` whole base tokens; `0` only on a pre-upgrade leg, meaning uncapped). `postInflow` fires if the asset carries `HOOK_POST_INFLOW` ([Hooks](/docs/5-1-3-hooks)). Emits `Deposited(sender, token, amt, lpAmt)` and `SolvencyUpdated(cWad, navBase, claimBase)`, and returns `DepositResult{lpAmount, actualDeposit, deadLp}`.

At $C > 1$ you buy less face than you pay: the incumbents' surplus is not for sale. At $C < 1$ you buy more, which is the reward for recapitalising. Either way the deposit leaves $C$ where it found it (`testFuzz_deposit_preserves_the_rate`). `reserves` and `liabilities` are `uint128`, and a deposit past that ceiling reverts `Overflow`.

A leg at `reserves == 0` with liabilities outstanding refuses deposits (`InvalidState`). `Pool.donate(token, amount)` is the reopen path: it credits `amt` to reserves and face `amt · 1e18 / C` to liabilities, raises the index and mints nothing, so the gift lands on the leg's LPs and $C$ does not move. It carries the same allowlist, band, weight and deposit-cap checks as a deposit and the same `FeedUnavailable` refusal. `donate` reverts `InvalidState` on a leg that was never credited (`liabilities == 0`), because reserves against an empty claim book would be claimed by the next depositor; open a leg by depositing.

Deposit is charged nothing: no protocol fee, no spread, no [coverage toll](/docs/glossary#coverage-toll). $C$ is not a charge; it prices the claim you buy.

## 3. The dead-share seed

The first depositor into a leg (receipt `totalSupply() == 0`) funds a permanent floor. `PoolLiquidity.seedDeadShares` does three things:

- computes `seed = 10**deadSeedPow10`, or `10**decimals / DEAD_SHARE_SEED_DIV` (`DEAD_SHARE_SEED_DIV` = 1000, i.e. 0.001 token) when `deadSeedPow10` is 0;
- mints `deadLp = ceil(seed * 1e18 / idx)` to `address(0)`;
- subtracts it from that depositor's own mint (`lpAmt -= deadLp`).

It emits `DeadSharesSeeded` and is paid once per leg, forever unburnable. Per-asset `deadSeedPow10` is capped at the leg's decimals plus `DEAD_SEED_POW10_HEADROOM` = 3.

Users cannot transfer a receipt to `address(0)` themselves: `LPToken._beforeTokenTransfer` reverts `ZeroAddr` unless the caller is the pool, since a user burn there would raise the dead floor with LP money.

## 4. The anti-JIT cooldown

| Parameter | Value | Site |
|---|---|---|
| `DEFAULT_FLOW_COOLDOWN` | 15 s, set at `Pool.initialize` | `PoolConstantsLib.sol` |
| `MAX_FLOW_COOLDOWN` | 300 s, ceiling on `PoolConfig.setFlowCooldown` | `PoolConstantsLib.sol` |
| Read at runtime | `Pool.flowCooldownSecs()` | `Pool.sol` |

The lock against [JIT liquidity](/docs/glossary#jit-just-in-time-liquidity) is armed at mint, on the receipt: `LPToken.mint` writes `locks[to] = {stamp: block.timestamp, frozen: previousFrozen + amount}`, resetting `frozen` to `amount` if the previous lock had already expired. It freezes a quantity, not the account; an older, unfrozen balance stays movable. Topping up inside an unexpired window restarts the clock over the whole recent parcel, and `swapLiability` arms a fresh lock on the destination shares.

Enforcement lives entirely in `LPToken._beforeTokenTransfer`, which reverts `CooldownActive` when `balanceOf(from) < amount + frozen` and `block.timestamp < stamp + flowCooldownSecs`. It gates the withdraw burn, the `swapLiability` burn and plain ERC-20 transfers of the receipt alike; `PoolLiquidity` performs no cooldown check of its own. Any lock older than `MAX_FLOW_COOLDOWN` short-circuits without reading the pool. Related block-level protections: [Flow Guards](/docs/3-2-1-flow-guards).

## 5. Withdraw

`Pool.withdraw(token, lpAmount, minAmountOut, deadline)` forwards to `PoolLiquidity.withdrawTo($, token, token, …)`; the same-asset case is literally `withdrawTo` with `tokenFrom == tokenTo`. Both entrypoints are `nonReentrant`, `whenInitialized` and `beforeDeadline`, and both endpoint legs are checked against `HALT_MASK`. No ERC-20 approval is needed: the pool burns the caller's own receipt.

Every exit settles against the pool rate

$$C = \frac{\sum_k R_k\, m_k}{\sum_k L_k\, m_k}$$

over every leg on the pool's roster, with $m_k$ the leg's [mark](/docs/glossary#mark-price) in base units (`PoolSolvency.solvency`, derivation in [Inventory Management §5](/docs/1-1-1-inventory-management#5-lp-settlement-at-the-pool-rate)). $C$ is uncapped: an over-solvent pool pays more than face.

Sequence, after `requireNoFlash` and the `lpAmount == 0` check:

1. **Face value.** `withdrawValue = lpAmount * assetFrom.liquidityIndexWad / 1e18`. Above `assetFrom.liabilities` reverts `InsufficientAmount`; zero reverts `ZeroValue`.
2. **Rate.** Same-asset: pay `face · min(c_leg, C)` ([§5.1](#51-same-asset-exit)). Cross-asset: convert fair value `face · C` ([§5.2](#52-cross-asset-exit-withdrawto)).
3. **Cross-asset only:** anchor-path conversion and mark cap ([§5.2](#52-cross-asset-exit-withdrawto)).
4. **Guards.** `amt == 0` reverts `ZeroValue` before any burn. `preOutflow` recalls from the yield hook for `amt + protoFee + minLiquidity`; after the ledger move, `liquidReserves < minLiquidity` reverts `ThresholdViolation`, as does `amt < minAmountOut`.
5. **Burn**, where the cooldown bites ([§4](#4-the-anti-jit-cooldown)), then settle and `PoolIOLib.push` the output, unwrapping to ETH for the NATIVE sentinel.

### 5.1. Same-asset exit

`PoolLiquidity._quoteWithdrawSame` pays `amt = face * mu / 1e18` with

$$\mu = \min\big(c_{\text{leg}},\ \text{cap}\big), \qquad c_{\text{leg}} = \frac{R \cdot 10^{18}}{L}, \qquad \text{cap} = \begin{cases} C & \text{every roster leg gateable} \\ \min(1,\ C_{\text{lastGood}}) & \text{otherwise} \end{cases}$$

(`PoolLiquidity.exitMu`, `PoolSolvency.exitCap`). $C$ is what you are owed. $c_{\text{leg}}$ is the in-kind delivery bound: a leg cannot hand out more of its own token than it holds, and $c_{\text{leg}}$ pro-rata is race-free where `face · C` out of a drained leg is a run. When $c_{\text{leg}} < C$ the pool is short of that token, not of value, and the rest of your `face · C` claim is reachable through `withdrawTo` (§5.2) at that conversion's spread.

What you receive:

- `face · μ` of your token; $\mu > 1$ when both the pool and your leg are over-covered.
- With any roster leg's mark unusable, the exit still settles, oracle-free, at `min(c_leg, min(1, lastGoodCWad))`, never more than the last observed healthy rate, or 1 on a pool that has never observed one (`lastGoodCWad == 0`).
- On a pool that predates the roster (`getLegs()` empty), `face · min(c_leg, 1)` until the owner's `GOVERNANCE`-tier `BACKFILL_LEGS` op arms it.
- `previewWithdraw` returns `(amountOut, haircut)` with [`haircut`](/docs/glossary#haircut) `= face - amt` when positive.

Derivation, rate invariants and the test roster: [Inventory Management §5](/docs/1-1-1-inventory-management#5-lp-settlement-at-the-pool-rate).

### 5.2. Cross-asset exit (`withdrawTo`)

`Pool.withdrawTo(tokenFrom, tokenTo, lpAmount, minAmountOut, deadline)` burns a receipt on one leg and pays out in another. Fair value is `face · C`, converted along the [anchor](/docs/glossary#anchor-tree) path by `Pricing.anchorPathQuoteLp` with the path's spread, `protoFee` and `lpFee` charged on the output leg as for a swap ([Anchor Path Pricing](/docs/1-1-3-anchor-path-pricing)), capped at the oracle mark path (`_markCap`), and depeg-checked on both legs and every hop (`priceBandGuardAll`). The from-leg must be physically backed (`requireBacked`), and its own coverage is never read, so slicing an exit buys nothing. There is no output-leg haircut: the claim was settled at $C$ on the way in. Any roster mark unusable reverts `FeedUnavailable`.

`Pool.swapLiability(tokenIn, tokenOut, lpAmountIn, minLpAmountOut, deadline)` is the receipt-to-receipt variant: the same `fairIn = liabIn · C` conversion, mark-capped, with the token output credited to the destination leg as face `amountOut · 1e18 / C`. It moves no reserves, is protocol-fee exempt, books no LP fee, requires `LIABILITY_SWAP_ENABLED_BIT` on both legs, and checks the weight and deposit caps on the credited leg as a deposit does; it is not gated by the depositor allowlist, since the caller already holds a claim. Full sequence: [Liability Swaps](/docs/4-2-liability-swaps).

### 5.3. Events

A same-asset withdraw emits one `Withdrawn(sender, fromTk, amt, lpAmount)`. A cross-asset `withdrawTo` emits **two**: `LiabilitySwapped(sender, fromTk, toTk, lpAmount, 0, haircut)` for the burn, where `haircut = face - face · C` when positive, and `Withdrawn(sender, toTk, amt, 0)` for the payout; a single `Withdrawn` cannot carry both legs. An indexer that treats `Withdrawn` as the whole story corrupts both legs' reconstructed balances. Every LP entrypoint on an armed pool also emits `SolvencyUpdated(cWad, navBase, claimBase)`, the numerator and denominator in base units, so $C(t)$ is reconstructable from logs without replaying marks.

## 6. What can stop you exiting

Three things gate an exit, and they do not gate the same paths. Verified against
`PoolLiquidity.withdrawTo` and `Pricing.anchorPathQuoteLp` at HEAD.

| Condition | Same-asset `withdraw` | Cross-asset `withdrawTo` | `swapLiability` |
|---|---|---|---|
| Any roster leg's oracle feed is stale or dead (`poolSolvencyWad()` returns `ok = false`) | not blocked, degraded rate | blocked | blocked |
| A halt bit is set on the leg | blocked | blocked | blocked |
| Executable reserves below `minLiquidity` | blocked | blocked | not applicable |

Row 1 scope: **any** leg on the roster with a non-empty book, not only the legs your exit touches,
because $C$ reads every mark. A same-asset withdraw needs no mark to settle: with one unusable it
pays `min(c_leg, min(1, lastGoodCWad))` (§5.1), so a stale oracle cannot trap a position in the leg
it was deposited into. Everything that credits a claim against $C$ refuses instead: deposits,
donations, cross exits, liability swaps and hook yield credits all revert `FeedUnavailable` until
every mark is usable again. There is no par fallback on any of them
([Inventory Management §5.2](/docs/1-1-1-inventory-management#52-where-the-rate-is-applied)).

A halt bit stops all three, including the same-asset path: the `HALT_MASK` check runs on both
endpoints of every withdraw. Halting is a guardian-or-owner action; un-halting is owner-only, so a
halt can be applied faster than it can be lifted. See
[Access Control](/docs/3-1-overview).

`maxRedeem` is a floor, not a promise: it is computed from executable liquidity at the time you read
it, and a withdraw re-checks it after any hook recall, so a large exit can revert even though the
app quoted it a moment earlier.

### 6.1. When the quote service is down

Deposits and withdrawals in the app need the price service: the recap's minimum received and the
send-time floor are both priced from its chain quote. When that service is unavailable the app
pauses LP writes, greys the last numbers it holds and says so on the form; it does not invent a
local price.

The pools stay permissionless, so this is a UX outage, not a custody one. Your receipt is still in
your wallet and the exit still exists on chain. To take it without the app, call
`Pool.previewWithdraw(token, lpAmount)` from a block explorer, then pass its `amountOut` as
`minAmountOut` to `Pool.withdraw(token, lpAmount, minAmountOut, deadline)`. A cross-asset exit uses
`Pool.withdrawTo(tokenFrom, tokenTo, lpAmount, minAmountOut, deadline)` the same way. The SDK
exposes both calls in `@btr-protocol/sdk`; see [Basic Operations](/docs/5-1-1-basic-operations).

---

## 7. What the position actually earns

Your claim grows through two numbers, not through a token balance that goes up. The leg's
`liquidityIndexWad` sets your face, and it moves on that leg alone: up with the LP share of swap
fees on that leg (net of `protoSharePct`, currently 20%), with donations and with hook yield, each
credited at $C$; down with `hookWriteDown`, which cuts the leg's liabilities by
`ceil(loss · 1e18 / C)` when a hooked venue loses money. The leg whose venue lost pays and no other
leg's claim moves; there is no protocol backstop for the loss ([Hooks](/docs/5-1-3-hooks)).

The pool rate $C$ sets what face is worth, and it moves for every LP in the pool at once: the
coverage toll and retained skew raise $R$ against unchanged $L$ on whichever leg they land, and a
real NAV loss on any leg, a mark-to-market LVR loss included, lowers it pro-rata for everyone. The
only bounds on how much of one leg's risk $C$ spreads are the two soft caps in its `RiskConfig`,
`maxLiabWeightBps` and `depositCapCode`, which bind when a claim is credited and drift with marks
afterwards ([Inventory Management §5.3](/docs/1-1-1-inventory-management#53-what-pooling-costs-and-what-bounds-it)).

The index includes no compensation for [LVR](/docs/glossary#lvr-loss-versus-rebalancing). AIMM
prices adverse selection into the spread so that flow pays for it rather than LPs subsidising it,
but nothing pays an LP back for it after the fact. A position's return is fees and tolls earned,
minus what the spread failed to charge.

---

## 8. In the app

The LP surface is the swap form: `/pools` → **Manage** writes `?pool=&asset=&action=deposit|withdraw|swap` into the URL and renders `<SwapForm mode="lp">` with `fromSym == toSym`, so the deposit/withdraw toggle is the same flip gesture as buy/sell.

- The withdraw amount field is in underlying units, converted to shares by `amt * WAD / index`, that is, by face. The exit multiplier $\mu$ of §5.1 applies on top, so you receive `face · μ`: less than the number you typed whenever $\mu < 1$.
- **Max / 75%** read `Pool.maxRedeem(owner, token)`, which folds `HALT_MASK`, a zero index, frozen shares from `LPToken.locks`, and `liquidReserves − minLiquidity` divided by `index · μ` into a share capacity. It ignores hook recall, depeg bands and `requireNoFlash`, so a call can still revert at a size `maxRedeem` allowed.
- The front currently sends `minAmountOut = 0` on both `withdraw` and `withdrawTo`, and a hardcoded `deadline = now + 600` (10 minutes). The recap's slippage row is display-only; the on-chain floor is zero. Marked testnet in the code; integrators must set their own `minAmountOut`.

## 9. Contract reference

| Call | Purpose |
|---|---|
| `Pool.deposit(token, amount)` | single-sided mint, returns `DepositResult` |
| `Pool.withdraw(token, lpAmount, minAmountOut, deadline)` | same-asset exit |
| `Pool.withdrawTo(tokenFrom, tokenTo, lpAmount, minAmountOut, deadline)` | cross-asset exit |
| `Pool.swapLiability(tokenIn, tokenOut, lpAmountIn, minLpAmountOut, deadline)` | receipt-to-receipt move |
| `Pool.donate(token, amount)` | credit a leg at $C$ without minting |
| `Pool.previewWithdraw(tk, lp)` | same-asset `(amountOut, haircut)` at `min(c_leg, C)`; indicative, arms no band guard |
| `Pool.maxRedeem(owner, tk)` | redeemable share capacity |
| `Pool.poolSolvencyWad()` | `(cWad, ok)`: the live $C$, and whether every roster mark was usable |
| `Pool.getLegs()` | the roster $C$ sums over; empty means unarmed |
| `Pool.getLPBalance(u, tk)` / `Pool.lpToken(tk)` | receipt balance / receipt address |
| `Pool.getAsset(tk)` / `Pool.getCoverageRatio(tk)` | leg ledger and $c$ |
| `Pool.flowCooldownSecs()` | live anti-JIT window |

SDK entry points are `deposit` / `withdraw` in `@btr-protocol/sdk` (`src/pool`), with EIP-5792 batch builders in `src/router`; see [Cookbook §3](/docs/5-2-3-cookbook#3-liquidity) and [Basic Operations](/docs/5-1-1-basic-operations).

## 10. LP checklist

Run before you commit capital. Every item is stated in full above.

**Before depositing**

- [ ] Read two ratios. $C$ from `Pool.poolSolvencyWad()` prices every claim in the pool, yours from the moment you deposit; a real loss on any leg moves it. $c = R/L$ on your leg shifts the quoted mid through the [inventory skew](/docs/glossary#inventory-skew) (saturating at $c \le \tfrac12$ and $c \ge 2$, fixed in code) and caps what a same-asset exit delivers in your own token ([§5.1](#51-same-asset-exit)).
- [ ] Deposit the asset you are willing to leave in. Same-asset `withdraw` is not feed-gated; cross-asset `withdrawTo` and `swapLiability` are, so a dead or depegged feed shuts those exits ([§6](#6-what-can-stop-you-exiting)).
- [ ] Check the fee split. $\texttt{protoSharePct} \in [0, 100]$ is the share of every swap and flash spread routed to `pool.treasury()`; the remainder raises your index. Set in `initdata` at `createPool`, moved afterwards by a `TUNING`-tier `UPDATE_FEES` with one hour of notice under `PROD_DELAYS`, so watch the queue ([Incentivization §2](/docs/5-3-3-incentivization#2-fee-split)).
- [ ] If the leg runs a hook, read which venue first ([Hooks](/docs/5-1-3-hooks)). You carry its credit risk with no insurance layer, and part of the leg's reserves sit off-pool: pricing uses full $R$, your withdrawal executes against $R_{liq}$. Check $R_{liq}$ against `minLiquidity`, not $R$: a leg can be solvent on paper and unwithdrawable in size in the same block.

**While the position is open**

- [ ] Fee APR is gross, not return. Realised return is the change in `liquidityIndexWad` net of the LVR paid to arbitrageurs against the external mark, and nothing rebates that ([§7](#7-what-the-position-actually-earns)). Judge a leg on realised index growth over a full cycle.
- [ ] Other LPs move your coverage without touching reserves. `swapLiability` re-denominates a liability from one leg to another at the oracle mark, moving no cash: your leg's coverage ratio, and your in-kind delivery bound, change with no reserve flow to watch for.
- [ ] You cannot pin a version. A `GOVERNANCE`-tier beacon swap at `PoolFactory` re-points every live pool at once. Your notice is the [timelock](/docs/glossary#timelock); your only response is to exit ([Admin](/docs/3-1-2-admin)).

**When you exit**

- [ ] Size from `Pool.maxRedeem`, not from your balance. It folds the halt bits, a wiped index, your frozen shares and $R_{liq} - \texttt{minLiquidity}$ into one number, and returns `0` rather than a partial answer when any of them binds. It assumes no hook recall succeeds, so it is a floor, not a promise ([§8](#8-in-the-app)).
- [ ] Read `LPToken.locks(holder)` and size from `balance − frozen`. The cooldown freezes the freshly minted quantity against `withdraw`, `withdrawTo`, `swapLiability` and plain `transfer`; a violation reverts `CooldownActive` ([§4](#4-the-anti-jit-cooldown)).
- [ ] A halt locks the door in both directions. `checkRiskFlags` gates `deposit`, `donate`, `withdrawTo` and `swapLiability` on `HALT_MASK`, so a halted leg cannot be exited by anyone at any price until the **owner**, never a guardian, clears the specific bit. This is the single largest discretionary risk an LP carries.
- [ ] Know your leg's caps. `Pool.getAsset(tk)` returns `depositCapCode` (leg notional cap in whole base tokens, `m·10^e` from `m<<4|e`), `maxLiabWeightBps` and `flags`; bit 8 set means deposits are allowlisted. A capped leg refuses a deposit that would carry it past the cap, and refuses every deposit while its own mark is unusable.

## 11. Related documentation

- [Inventory Management](/docs/1-1-1-inventory-management#5-lp-settlement-at-the-pool-rate): coverage ratio and the pool rate in the pricing context
- [Spread & Fees](/docs/1-1-4-spread-fees#5-the-fee): how the LP fee reaches `liquidityIndexWad`
- [Pool](/docs/1-2-1-pool): the module, its storage and its full ABI
- [Flow Guards](/docs/3-2-1-flow-guards): cooldowns and block-level protections
- [Invariants](/docs/1-1-8-invariants#i-14-executable-liquidity-floor-not-raw-reserves): the liquidity floor a withdrawal must respect
