> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rekt.money/llms.txt
> Use this file to discover all available pages before exploring further.

# Providing liquidity

> One shared pool takes the other side of every position and funds every win. How a win is paid, how the pool is read, how a redemption works and how the pool loses money.

There is one shared pool. It takes the other side of every position on every market, funds every
win, and earns the fees traders pay. Its holders are the counterparty.

<Warning>
  **LP losses are real and they are not theoretical.** The pool funds every winning position out
  of its own assets. A run of trader wins draws it down, and your shares are worth less
  afterwards. Nothing about the design prevents that. A win larger than the pool can pay at once is
  paid in order, from a queue: see [how a win is paid](#how-a-win-is-paid).
</Warning>

<Note>
  **Deposits to the pool are closed.** A deposit is refused (`LpDepositsClosed`). A redemption is
  refused unless the governed flag allows withdrawals, or the venue is in a [wind-down](#wind-down)
  with no stake live. A win that the pool cannot pay in full at once is paid in order, from a queue:
  see [how a win is paid](#how-a-win-is-paid).
</Note>

## How shares work

A holder of the pool owns **shares**. Shares are a claim on a fraction of the pool's assets, not on
a fixed amount of dollars. As the pool earns fees the share price rises; as it funds wins the share
price falls. A holder is not lending: they hold a position in the book.

```ts theme={"dark"}
import { vaultGauges, type VaultView } from '@rekt/1000x';

export function sharePrice(vault: VaultView, tokenBalance: bigint) {
  const gauges = vaultGauges(vault, tokenBalance);
  // LP assets per share, scaled so it is exact in integers.
  return gauges.sharePriceMicro;
}
```

## The four numbers that describe the pool

Read them together. Any one of them alone will mislead you.

| Gauge | What it means |
| - | - |
| `lpAssets` | The pool's assets that belong to LPs: the denominator of the share price. It is the vault's token balance less any payout already settled and owed to a named trader, and nothing else. |
| `withdrawable` | What may leave the pool at all: the free cash. It is `lpAssets` less every stake, less the money of the queue that is funded and not yet delivered, and less the cover the venue needs. **This is the gauge your exit is measured against** — see [the guarantee](#the-guarantee-you-can-only-withdraw-capital-that-is-not-backing-a-position). |
| `openable` | `withdrawable` less the value of the shares on **armed** tickets. Only arming a ticket raises that hold, so with nothing armed `openable` equals `withdrawable`. It does not gate an open: a position is admitted at its fill against a rule applied per market and in total. |
| `sharePriceMicro` | LP assets per share, scaled so it is exact in integers. |

A redemption needs the queue funded (`QueueNotFunded`). `lpAssets` does **not** subtract what the queue
still owes: before you read a share price, subtract the unfunded queue and the parked amount.

The gap between `withdrawable` and `openable` is the value of the shares on **armed** tickets, and
nothing else. A requested ticket that has not been armed sets nothing aside. Arming is refused
while the pool can already pay the claim (`RedemptionPayableNow`), so the hold begins only once the
claim exceeds `withdrawable`. See the **Arm** bullet below.

```ts theme={"dark"}
import { vaultPda, decodeVault, vaultGauges } from '@rekt/1000x';
import { Connection, PublicKey } from '@solana/web3.js';

export async function poolGauges(connection: Connection, vaultTokenBalance: bigint) {
  const info = await connection.getAccountInfo(vaultPda().address, 'confirmed');
  if (!info) throw new Error('the vault account does not exist');
  const vault = decodeVault(new Uint8Array(info.data));
  const gauges = vaultGauges(vault, vaultTokenBalance);
  // `vault.tokenAccount` is the SPL account whose balance you passed in — read it, do not guess.
  void new PublicKey(Buffer.from(vault.tokenAccount, 'hex'));
  return gauges;
}
```

## How a win is paid

**A win is paid in full. Part of it may be paid later, in order. There is no guaranteed time. The
amount queued ahead of you is shown.**

* **Opening takes the stake and nothing else.** The venue decides at the fill whether a position
  fits, against a rule the program applies per market and in total. An order that does not fit is
  returned with the whole stake and no fee. A hedge, a smaller target and a close are never refused
  by it.
* **At a close the stake comes back at once.** The profit is paid from the pool's free cash when no
  earlier claim is unfunded; whatever is left joins the queue. The settlement events say how much was
  paid now and how much was queued.
* **The queue is first in, first out, and cash that other positions leave in the pool funds it.**
  The protocol's own share of a close is paid last, and not at all while any claim is unfunded.
* **Anyone can deliver funded money** with `pay_queue`, in line order, to the wallet recorded on the
  account. Money for an account that cannot receive it is held for that account under the unclaimed
  rule below.
* **When the line is nearly full, a second unpaid win of the same account waits behind its first.**
* **A book with an unpaid entry cannot close its account** until it is paid.

A client shows what happened and where the account stands, never a forecast: at a close, the amount
paid now and the amount queued; while queued, the amount ahead, the entries ahead, what has been
funded, what has been delivered and the entry's age. It does not show an estimate of a wait.

## The guarantee: you can only withdraw capital that is not backing a position

A redemption pays at most the free cash: not a stake, not funded queue money, and not the cover the
venue needs. It needs the queue funded, and it is refused while withdrawals are not enabled.

This is the one property to hold on to, and it is a statement about the pool's **balance sheet**,
not about any clock:

```text theme={"dark"}
withdrawable = lpAssets − stakes − funded queue money − cover the venue needs
execute_redeem:  payout must be ≤ withdrawable, or the program refuses  →  WouldStrandReserve
```

**An LP can only ever withdraw capital that is not backing a live position.** Not *once the delay
has run* — always, on every attempt, at every parameter setting. You cannot run from a position's
outcome using the money that position collateralises, and it makes no difference whether that
position lives thirty minutes, a day, or is never closed at all. The subtraction is performed at
the instant of payment, and no tenor value appears anywhere in it.

Three consequences worth stating separately, because each is a question LPs ask:

* **The cover rule and your redemption draw on one pool of free cash.** A fill is admitted only
  inside the free cash and the floors, and a redemption is paid only out of the free cash. Cash the
  venue needs as cover is not free, for either, so neither can take what the other is not allowed
  to keep.
* **A refused execution does not consume your ticket.** If positions opened during your wait raised
  the cover the venue needs, the payout is refused and the ticket survives. Retry after positions
  settle. The cover is never the thing that yields.
* **It is not a promise the pool has money.** The guarantee bounds *what you may take out of a
  pool of a given size*. If traders have won and the pool is smaller, your shares are worth less,
  and nothing here recreates money the pool has paid out.

The withdrawal delay does not depend on the tenor. The balance-sheet rule above is what keeps a
redemption from taking capital that backs a live position, on every attempt.

## Wind-down

The operator can wind the venue down: new orders, adds and target raises stop, positions still end by
their own rules, and redemptions open once no stake is live and the queue is funded.

## Getting your money out

Withdrawal is not instant, and the wait is the point.

<Steps>
  <Step title="Request">
    You burn nothing yet — you create a redemption ticket recording the shares and the time. Your
    shares stay in the pool's denominator for the whole wait, so you keep taking gains and losses
    like any other holder until the moment you are paid.
  </Step>

  <Step title="Wait for maturity">
    Your wait is `max(the delay frozen onto your ticket, the protocol's live floor)`.

    **`effectiveWithdrawalDelay(vault, config)` is the delay a NEW request would be given, and it
    is not the delay any existing ticket serves.** Reading it as one is the mistake this split
    exists to prevent: the two differ for every ticket that outlived a governance change. For a
    ticket you already hold, either take `max(ticket.delayS, minWithdrawalDelay(config.params))` —
    both exported — or, better, call `redeemGates`, which computes maturity from the ticket's own
    frozen delay and reports how much of the wait is left.

    Two halves, governed differently, and the difference is the whole of what an authority can do
    to a wait you have already begun:

    * **The dial is frozen at request.** `request_redeem` stamps the vault's withdrawal delay onto
      your ticket. Governance may then move that dial, and it reaches only requests made afterwards.
      It cannot lengthen your ticket through this half.
    * **The floor is read live, and it may not fall.** The floor is **one hour**. A governance change
      that would *lower* it is refused outright, which is what stops a pending ticket from being
      stranded. Arming a ticket freezes the floor onto it.

    What the floor buys, stated exactly: opens are refused inside a scheduled event window, so an
    exit that completes inside an event window was requested at or before that window opened, while
    the venue was still open to everybody. It does not say you know nothing others do not. It says
    the decision to leave was taken while the book could still move against you.
  </Step>

  <Step title="Execute, inside the window">
    A matured ticket is executable for a fixed window, frozen onto your ticket at request the same
    way the delay dial is. The window is at least **six hours** (`TICKET_VALIDITY_S`); governance may
    lengthen it but never shorten it. Read the dial on the vault (`vault.ticketValidityS`). Miss the
    window and the ticket can be reaped by anyone. Your rent still comes back to you, never to the
    reaper.

    An execution can also be refused on capacity, by the balance-sheet gate above. That refusal
    leaves the ticket intact and executable for the rest of its window.
  </Step>
</Steps>

## What you are actually paid

**Your payout is capped at the pool's value when you requested, and it is not capped on the way
down.** The instant you call `request_redeem`, the protocol records what your shares were worth
right then — priced against LP assets **net of the trader collateral currently in flight**, which
is a slightly lower figure than the raw share price and is the direction that can never overdraw.
**Executing** that ticket pays **the lower of that recorded value and the pool's value at the
moment you execute**:

| While you waited, the pool... | You are paid |
| - | - |
| rose | the value at the moment you requested — not the higher, current value |
| stayed flat | the same either way |
| fell | the current, lower value — in full, not cushioned |

**Cancelling is priced differently, and this is easy to miss.** A cancel returns **every share**,
always — whether the pool rose, fell or stayed flat. Value moves at exactly one instant, and that
instant is the execution.

What a cancel does cost you is your **next** request. The ceiling recorded on the cancelled ticket
carries onto your account and caps the next redemption you request, until the original ticket's
window would have closed. So a cancel is not free, and it is not a markdown of the shares you get
back — it is a markdown of what you can ask for next.

The practical consequence: requesting a redemption you might
cancel is not free optionality above the ceiling, only below it.

**One exception: if your TICKET carries every share in the pool, there is no ceiling.** The test is `ticket.shares == total_shares`, and `total_shares` counts every live ticket's shares
as well as every LP account's. So a sole holder who requests **part** of their holding is capped
at that ticket's recorded value like anyone else, and a second live ticket held by anybody makes
the test false. The exception is read on exactly two paths — executing and arming. The cancel path
takes no cap at all, for any LP: every share comes back, as the rule above already says.
The moment a second holder has shares, the ordinary ceiling above applies to everyone again.

Two escapes exist and both are worth understanding before you need them:

* **Arm.** If your matured ticket is worth more than the pool can currently pay, you may *arm* it,
  which gives it priority over every new open until it is paid. The price of arming is disclosed
  up front: an armed ticket can be executed by anybody, and cannot be cancelled until it is paid
  or expires. You cannot arm a ticket the pool can already pay — at that moment the honest
  instruction is simply to execute.
* **Cancel.** The owner may cancel an unarmed ticket at any time. Every share returns; the
  ticket's recorded ceiling then caps your next request until that ticket's window would have
  closed.
  After the window closes, anyone may reap it.

The SDK predicts each of these before you send it, with the measured value and the threshold:

```ts theme={"dark"}
import {
  armGates,
  cancelGates,
  redeemGates,
  type ConfigView,
  type GateReport,
  type VaultGauges,
  type VaultView,
} from '@rekt/1000x';

// All seven are REQUIRED. `unbondingValue`, `delayS` and `validityS` are frozen onto the ticket at
// request, and `frozenFloorS` when it is armed (0 until then). None has a fallback to the vault's
// live figures: a default would be wrong for a ticket that outlived a governance change.
interface Ticket {
  shares: bigint;
  requestedAt: bigint;
  armed: boolean;
  unbondingValue: bigint;
  delayS: number;
  validityS: number;
  frozenFloorS: number;
}

export function redemptionReports(
  config: ConfigView,
  vault: VaultView,
  gauges: VaultGauges,
  ticket: Ticket,
  nowUnix: bigint,
  signer: string,
  ticketOwner: string,
): Record<string, GateReport> {
  const ctx = { config, vault, gauges, ticket, nowUnix };
  return {
    execute: redeemGates(ctx),
    arm: armGates(ctx),
    cancel: cancelGates({ ...ctx, signer, ticketOwner }),
  };
}
```

## How you actually lose money

Stated plainly, because "impermanent loss" is not the risk here and borrowing that vocabulary
would mislead you:

* **Traders win.** The pool pays. Over a run of favourable outcomes for traders, the share price
  falls, and it may not come back.
* **The pool is small.** A small pool is a concentrated one: a handful of maximum-size wins is a
  large fraction of it.
* **Nothing will catch you.** There is no insurance fund, no backstop, no protocol treasury
  commitment and no third party standing behind a balance. See [the insurance fund](#the-insurance-fund).
* **Parameters are governed.** Fees, bounds, caps and the withdrawal delay can be changed on
  chain by the protocol's authority. Every change is public and readable; none of them requires
  your consent.
* **This is unaudited software.**

## What you earn

Four things reach the pool, and they are not the same kind of income:

* **A share of the entry fee and of the exit fee.** Traders pay an entry fee on every open and on
  every increment they add later (read `config.params.entryFeeBps`; it is 0, so opens are free), and
  an exit fee on most closes (not on one paid at the barrier). The exit fee is a share of the exit
  value, higher on a loss than on a win; the rates are on [Fees and limits](/limits).
* **A share of what losing positions forfeit.** The loss tax is divided between the pool and the
  protocol. The pool is the residual claimant on the loss tax and on both fee splits, so it takes
  whatever the named shares do not, and the rounding dust lands here.
* **The borrow fee, in full.** It is rent on the pool capital a position freezes, so all of it goes to
  the pool. While its rate is zero it collects nothing: [Fees and limits](/limits) has the rate.
* **An unclaimed payout that expires.** See below.

What that adds up to for you depends on how much is traded, at what sizes, against a pool of what
size — read it from the on-chain accounts the SDK exposes, live, rather than from a number on this
page.

### An expired unclaimed payout is released to the pool

When a settlement cannot deliver a payout — the trader closed or froze their USDC account — the
money is escrowed inside the vault and held against that trader's name. It sits **outside**
`lpAssets` for the whole of that time, so it is not yours and it is not priced into your share.

If nobody claims it before `config.params.unclaimedPayoutExpiryS` has passed, anyone may sweep
it, and the sweep releases the claim rather than moving any tokens. The money was already in the
vault; dropping the obligation raises `lpAssets` by exactly that amount.

## The insurance fund

There is no insurance fund standing behind the pool, and no rule obliges the protocol to cover an LP's
loss. **Do not size a holding in the pool as though anything will catch you.**

## Before you rely on the pool

<Card title="Read the risk disclosure" icon="triangle-exclamation" href="/risk">
  The complete list of what can go wrong, for the pool and for traders, in plain language.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.