<!--
Sitemap:
- [Welcome to River](/index)
- [Asset-Backed Finance Primer](/concepts/finance-primer)
- [River for Lenders](/lenders/introduction)
- [Depositing](/lenders/depositing)
- [Earning: the Coupon](/lenders/earning)
- [The Accumulating Wrapper](/lenders/wrapper)
- [Withdrawals & Exits](/lenders/withdrawals)
- [Losses & Impairments](/lenders/losses)
- [FAQ](/lenders/faq)
- [Integrate with River](/integrate/get-started)
- [Wrapper Integration](/integrate/wrapper)
- [Class Token Integration](/integrate/class-token)
- [Contract Addresses](/integrate/addresses)
- [Protocol Architecture](/architecture)
- [Actors: Roles & Permissions](/technical/actors)
- [Proxies & Upgradeability](/technical/proxies)
- [Strategies & Reporters](/technical/strategies)
- [Glossary](/concepts/glossary)
- [Dual NAV & Exchange Rates](/accounting/dual-nav)
- [Settlement & Conservation](/accounting/settlement)
- [The Coupon Ledger](/accounting/coupon-ledger)
- [Wrapper Accounting](/accounting/wrapper)
- [Accounting Examples](/accounting/examples)
- [The Tiered Waterfall](/waterfall/)
- [Waterfall Scenarios](/waterfall/scenarios)
- [Security Model](/security)
- [Protocol Invariants](/technical/invariants)
- [List of Assumptions](/technical/assumptions)
- [External Entry Points](/technical/entry-points)
- [Governance](/governance)
- [Contract Reference](/reference/contracts)
- [River Engineering Standards](/reference/engineering-standards)
-->

# The Coupon Ledger

## The problem it solves

Interest in River is **known continuously but paid lumpily**. The servicer computes interest
off-chain from the credit book every day; the actual cash shows up on-chain in periodic lumps
(say, monthly), and sometimes less arrives than was earned. Meanwhile lenders are not standing
still: they buy, sell, enter, and exit mid-month.

That combination creates two ways to get coupon accounting wrong:

1. **Insolvency** — letting someone claim coupon before the cash backing it exists. If a month's
   interest is "credited" as claimable on day 1 and the cash arrives on day 30 (or arrives short),
   early claimers are paid with money that belongs to someone else.
2. **Timing theft** — attributing a period's interest by who holds at a single snapshot. If the
   month's coupon goes to whoever holds shares at the payment moment, buying one block before the
   payment captures a month of someone else's interest; selling one block before it forfeits yours.

`CouponLedgerLib` — the ledger shared by every distributing token — solves both with one design:
**accrue soft, fund final**. Entitlement is metered continuously as it is earned; cash truth is
reconciled once per interval, when it arrives.

## The mental model: a meter and a till

Think of two separate devices:

* **The meter** (`accrue`) runs continuously. Each time the servicer reports interest earned, the
  meter advances *per share*: `amount ÷ shares outstanding at that moment`. Your entitlement is
  your balance × the meter movement **while you held**. This is why the meter divides at every
  report rather than once at payment: a lender who held for the first 20 days of a 30-day month
  accumulated exactly the first 20 days of meter movement, no matter what happens afterwards.
  Meter readings are a *receivable* — display only, never claimable, never redeemable.
* **The till** (`fund`) opens once per interval, when cash arrives. It compares the cash to what
  the meter promised for the interval and prices the interval at

  ```
  rho = min(1, cash delivered / interval's accrued promise)
  ```

  Everyone who ran the meter during that interval realizes `their accrual × rho`. Only what has
  passed through the till is ever claimable, so the pool of claimable coupon is always backed by
  cash actually held — a lump that arrives short (`rho < 1`) haircuts exactly the people who
  earned during that interval, pro-rata, and cannot touch principal, previous intervals, or
  future ones. Offered cash is never stranded either: funding pulls only
  `min(cash, interval accrued)`.

## One interval, start to finish

```
Day 0    Interval opens (previous one was just funded).
Days 1–30  Servicer accrues daily. The meter advances; every holder's receivable
           grows in proportion to balance-at-each-accrue. Nothing is claimable.
Day 30   Servicer funds the month's cash.
         · rho = min(1, cash / interval accrued)
         · every interval participant's receivable × rho becomes CLAIMABLE cash
         · the funded-coupon index (cumCashIndex) steps up by (Δmeter × rho)
         · a fresh interval opens at zero
Any day  A holder claims: their settled claimable is paid out. Claim timing is
         economically irrelevant — claimable only changes at funds, and unclaimed
         coupon neither compounds nor decays.
```

## The three quantities

| Quantity | Meaning | Moves when |
| --- | --- | --- |
| `accIndex` | The meter: cumulative accrued entitlement per share (monotonic) | Every `accrue(amount)`: `+= amount / supply` |
| `intervalAccrued` | The open interval's total promise awaiting cash | Grows on accrue, zeroes on fund |
| `cumCashIndex` | The till record: cumulative **funded** coupon per share (monotonic) | Only on `fund`: `+= (accIndex − prevAccIndex) × rho` |

`cumCashIndex` is the load-bearing one for everything downstream: it is the per-share history of
coupon that actually got backed by cash, telescoped across every interval and every `rho`.

## Settlement: why holder timing is safe

Every balance change — transfer, mint, burn — **settles both parties first**, at their pre-change
balances. Settling means "bank the meter": the holder's receivable-to-date is written down against
their old balance, and their snapshot moves to the current meter reading. Concretely, on the
distributing class token:

* **Buy mid-interval (day 20 of 30).** The seller's first-20-days accrual is banked to the seller
  at the moment of transfer. Your meter starts at day 20. At the fund you realize 10 days × rho;
  the seller realizes their 20 days × rho. Nobody can buy a coupon by timing a purchase.
* **Sell mid-interval — the haircut follows the earner, not the holder.** Selling does *not*
  crystallize your accrual into cash. Your banked days 0–20 remain a receivable until the interval
  funds, and they are then priced at that fund's `rho`. If the month funds short, you bear the
  haircut on what you earned even though you no longer hold. Symmetrically, the buyer cannot be
  charged for interest they didn't earn.
* **Hold through many intervals without claiming.** Fine, and O(1) to settle later: per-holder
  state is two snapshots (last-seen meter reading and last-seen fund count), and a claim is one
  entry-interval term (your slice of the fund that closed the interval you entered in, at that
  fund's `rho`) plus one telescoped term (`balance × ΔcumCashIndex` across all completed intervals
  since). No loops over funds or holders, however long you waited.
* **Claim whenever.** Early and late claimers are treated identically; claimable changes only at
  funds.
* **Queue an exit.** `requestRedeem` locks shares *in place* — a queued exiter's shares keep
  running the meter and keep realizing at funds until the exit is actually FILLED. The wait is not
  unpaid, and the moment of filling — not claiming — is when earning stops (see
  [fill checkpoints](#fill-checkpoints) below and the wrapper's
  [exit-coupon clock](/accounting/wrapper#the-exit-coupon-clock)).

The one timing exposure that *is* real and intended: **everyone who accrues inside an interval
shares that interval's `rho`.** Interest earned early in a month is not safer than interest earned
late in it; the interval is priced as a unit when its cash arrives. This is the loss-sharing
policy for under-delivered interest, and it deliberately cannot be escaped by trading.

## Solvency by construction

Only `fund` adds to the claimable pool, and it adds exactly the cash it pulls. Floor rounding
throughout leaves dust in the pool, never a deficit:

```
Σ (all holders' claimable coupon)  ≤  couponCash held    
```

## Worked example: two holders, a transfer, a shortfall

```
Supply 200: Alice 100, Bob 100.

accrue(4)                      accIndex += 4/200 = 0.02/share
                               intervalAccrued = 4
                               Alice accrued (display): 2   Bob: 2

Alice sells 50 shares to Carol — both settle first:
                               Alice keeps her accrued 2 (banked at balance 100)
                               Carol starts from the current meter: 0

accrue(2)                      accIndex += 0.01/share
                               Alice +0.5   Bob +1   Carol +0.5
                               intervalAccrued = 6

fund(3)                        rho = 3/6 = 0.5 — a 50% shortfall
                               cumCashIndex += (0.03 − 0) × 0.5 = 0.015/share

Claimable:                     Alice (2 + 0.5) × 0.5 = 1.25
                               Bob   (2 + 1)   × 0.5 = 1.50
                               Carol (0.5)     × 0.5 = 0.25
                               Total 3.00 = exactly the cash funded ✓
```

Note what the shortfall did *not* do: it did not touch Alice's pre-transfer earnings (banked, but
still rho-priced — she earned them in this interval), did not give Carol anything from before her
entry, and did not touch anyone's principal.

## The same coupon, two faces: class token vs the 4626 wrapper

The distributing class token and the accumulating wrapper consume the identical ledger but expose
opposite experiences. On the class token, coupon accounting is **personal**: your entitlement is a
ledger entry against your address, settled around your own transfers, claimed as cash by you. On
the wrapper, coupon is **collective**: the wrapper itself is one big holder of the class token,
and everything it earns lands in one shared share price.

That shift moves where timing pressure lives:

| Event | Distributing class token (income face) | 4626 wrapper (accumulating face) |
| --- | --- | --- |
| Servicer accrues | Your personal receivable grows (display only) | Redeemable PPS unmoved; the **gross** (entry) price grows |
| Servicer funds | Your receivable × rho becomes claimable cash | Redeemable PPS **steps up**; the liquid slice becomes skimmable via rations |
| Buy mid-interval | Settlement banks the seller's accrual; you earn from entry — timing-safe automatically | You pay the **gross** price including pending accrual, so the coming PPS step is a wash — timing-safe *because entry is priced optimistically* ([dual NAV](/accounting/dual-nav)) |
| Sell mid-interval | Your banked accrual stays yours, priced at the interval's rho | The pending accrual is in the gross price the buyer pays you; the buyer inherits the step (and the seller's unspent ration rides pro-rata with the shares) |
| Under-funded interval (`rho < 1`) | Haircuts exactly that interval's earners, personally | Haircuts the collective: the PPS step is smaller than gross advertised; a mid-interval entrant loses at most `optimism × pending accrual` |
| Take income as cash | `claimCoupon()` — pure income, shares untouched | **Skim**: burn a sliver of shares against your cash ration ([wrapper accounting](/accounting/wrapper#cash-rations-and-skimming)) |
| Queued exit | Locked shares keep earning until FILLED | Same policy, metered per bucket by `cumCashIndex` with fill checkpoints |

The short version: the class token makes timing safe with **per-holder settlement**; the wrapper
makes timing safe with **pricing** (gross in, realized out) plus **rations** for the cash door.
Both inherit interval-level `rho` risk.

## `cumCashIndex` as a public seam

`cumCashIndex` — funded coupon per share — is consumed by the
[accumulating wrapper](/accounting/wrapper) to meter coupon owed to queued exiters. External
consumers rely on three properties, all guaranteed by the library: it is monotonic, it advances
only on `fund`, and `balance × ΔcumCashIndex` never exceeds the holder-level realized amount for the
same window.

## Fill checkpoints

A lazy consumer metering `shares × ΔcumCashIndex` has a blind spot: shares that stopped earning
mid-window (an exit fill burns them) would still be charged the full window. The ledger closes it
at the source. `checkpointFill(owner, controller, shares)` — called by the class token at every
`fillExit` — records two **monotone** accumulators, per exit owner and per controller:

```
filled     += shares                    Σ shares ever filled
fillWeight += shares × cumCashIndex     Σ shares × index-at-fill
```

A consumer snapshotting both at each settlement can then compute, for any window, exactly the
coupon its fills did **not** earn past their fill points —
`Δfilled × cumNow − ΔfillWeight = Σ q × (cumNow − c_fill)` — without ever observing individual
fills. How the wrapper consumes them is worked through in [Wrapper Accounting](/accounting/wrapper) and, step by step,
in `docs/audit/exit-coupon-settlement.md`.
