An honest loss

Download Markdown

An honest loss is the simplest failure: a protocol really loses money and its adapter reports the lower value. A borrower defaults, a token's redemption price falls, or an issuer writes its assets down. The vault sees the new number at the next refresh and books it automatically. Nobody has to act.

The examples start from the example vault: Alice holds 4,000 USDC of Senior, June holds 1,000 USDC of Junior, 3,000 USDC is invested in one leg, and the minimum coverage is 20%. Each share is worth 1 USDC at the start.

Why losses are booked at once

The vault books a drop in value immediately and a rise slowly (see Wrong or manipulated data). This asymmetry is deliberate. If a loss were booked late, people who leave before it is booked would be paid too much, and the people who stay would pay for it. Booking it at once keeps every share priced at what the vault can actually stand behind.

How it works

  1. Anyone calls refresh_leg. The vault asks the leg's adapter for a valuation report through a cross-program invocation (a CPI, meaning one program calling another).
  2. The vault stores the new value for that leg. A drop is recognised in full.
  3. Anyone calls sync_vault, or a deposit or withdrawal does it automatically. The vault compares its net asset value (NAV: idle cash plus all leg values) with what is booked in the two tranches.
  4. If NAV is lower, the difference is a loss. It is taken from Junior's value first. Only what Junior cannot absorb is taken from Senior's value, and that part is also recorded as Senior's impairment: a loss that is tracked so it can be repaid if value comes back.
  5. The vault enters Recovery if the loss reaches Senior, or if a single update books a loss larger than the vault's incident threshold. The threshold is a share of the booked value, set by the curator. The program's default is 25 basis points; a basis point is 0.01%, so 25 basis points is 0.25%.
sequenceDiagram
    participant K as Anyone (keeper or app)
    participant V as akashi_vault program
    participant A as Leg adapter
    participant P as External protocol
    K->>V: refresh_leg
    V->>A: CPI aai_value
    A->>P: read protocol accounts
    A-->>V: ValuationReport (value_low, value_mid, value_high, flags)
    V->>V: record the new value (a drop counts in full)
    K->>V: sync_vault
    V->>V: loss = booked value - NAV
    V->>V: Junior first, then Senior
    alt loss reaches Senior or exceeds the incident threshold
        V->>V: state = Recovery, emit IncidentEntered
    end

Figure: how a reported loss travels from the protocol into the tranche balances in two permissionless instructions.

The takeaway is that no special role is needed. The same two public instructions that book interest also book a loss.

Example 1: a tiny loss

The leg loses 0.1% of its 3,000 USDC: 3,000 × 0.001 = 3 USDC.

  • Junior falls from 1,000 to 997. Senior stays at 4,000.
  • The incident threshold is 0.25% of the 5,000 USDC booked value: 5,000 × 0.0025 = 12.50 USDC. A 3 USDC loss is below it, so the vault stays Active.
  • Senior withdrawals, new deposits and new investments keep working.

Example 2: a loss Junior can cover

The leg loses 20% of its 3,000 USDC: 3,000 × 0.20 = 600 USDC.

BeforeAfter
Junior1,0001,000 − 600 = 400
Senior4,0004,000
Junior impairment0600
Vault stateActiveRecovery

Table: a 600 USDC loss is taken entirely from Junior and starts Recovery, because 600 USDC is far above the 12.50 USDC threshold.

What still works in Recovery:

  • Senior can leave at full value. Alice withdraws 1,000 Senior shares. Senior lost nothing, so each share is still worth 1 USDC, and she receives 1,000 USDC.
  • Junior must wait. Coverage is now 400 ÷ (400 + 4,000) = 400 ÷ 4,400, about 9%. That is below the 20% minimum, so a Junior withdrawal fails with CoverageViolation.
  • No new money comes in. A deposit fails with VaultNotActive, and so does a new investment by the executor, the service that moves the vault's cash into and out of legs.

The vault records the 600 USDC as Junior's impairment. If the value comes back later, it repays Junior before anything is counted as new profit (Example 5).

Example 3: a loss equal to Junior

The leg loses exactly 1,000 USDC, the whole of Junior.

  • On a lending leg, Junior falls to 0 and Senior stays at 4,000. Senior is not impaired, so it can still leave at full value.
  • On a leg with a haircut, the result is different. A haircut means the adapter reports a low value below the book value, because selling the position now would cost something. The Maple and OnRe adapters put the low value 2% below book, Hastra and Solstice 1% below, and Huma 0.5% below. Suppose that after the loss such a leg has a book value of 1,500 USDC, and a 2% haircut gives a low value of 1,500 × 0.98 = 1,470 USDC. Withdrawals are priced at the low value, so the 1,500 − 1,470 = 30 USDC gap now falls on Senior. The vault is frozen, meaning no withdrawal can be funded: even Senior withdrawals fail with VaultFrozen.

The takeaway is that "Junior absorbs the loss" is not the whole story. Withdrawals are priced at the cautious low end of each leg's band, and that gap also comes out of Junior first. When Junior is gone, the gap reaches Senior and funding stops.

Example 4: a loss larger than Junior

The leg loses 50% of its 3,000 USDC: 3,000 × 0.50 = 1,500 USDC.

BeforeAfter
Junior1,0000
Senior4,0004,000 − 500 = 3,500
Senior impairment0500
Vault stateActiveRecovery, frozen

Table: Junior absorbs the first 1,000 USDC of the 1,500 USDC loss, and Senior the remaining 500.

Alice's withdrawal request cannot be funded (VaultFrozen). She cancels it to get her shares back. The admin then starts Settlement, the final wind-down of the vault:

  1. The executor takes the remaining 3,000 − 1,500 = 1,500 USDC out of the leg.
  2. Anyone calls distribute_settlement_cash. Idle cash is now 2,000 + 1,500 = 3,500 USDC.
  3. Senior's entitlement is its value plus its impairment: 3,500 + 500 = 4,000. Cash is paid Senior first, and 3,500 is less than 4,000, so all 3,500 goes to Senior.
  4. Alice converts her shares with settle_shares and receives 3,500 USDC. June receives 0.

Example 5: a dip that comes back

The leg loses 600 USDC, then the protocol's value comes back.

  1. After the drop: Junior 400, Junior impairment 600, vault in Recovery (as in Example 2).
  2. After the comeback: the leg is again worth 3,000. During Recovery, returning value first repays Senior's impairment (here 0), then Junior's. So Junior goes back to 400 + 600 = 1,000 and its impairment to 0. Nothing is split as profit.
  3. The vault stays in Recovery until the admin calls resume.

If only 300 of the 600 USDC come back, Junior ends at 400 + 300 = 700 and its impairment at 600 − 300 = 300. When the admin resumes the vault with resume, the remaining 300 is written off for good, because resume resets Junior's impairment to 0. Profit earned after that is split normally again.

Example 6: a slow loss

The leg loses 0.2% ten times in a row. Each step is about 3,000 × 0.002 = 6 USDC, below the 12.50 USDC threshold, so the vault never enters Recovery. After ten steps Junior has lost about 10 × 6 = 60 USDC and is worth about 940.

Coverage started at exactly the 20% minimum, so any Junior loss pushes it below: 940 ÷ (940 + 4,000) is about 19%. June can no longer withdraw (CoverageViolation), even though the vault is Active. The takeaway: a Junior exit can be blocked without any incident.

Example 7: the asset really loses value

Tests on copies of the real protocols lower the underlying price by 5%: Maple's exchange rate for the Maple leg, OnRe's net asset value for the OnRe leg, Hastra's stake price, and the oracle or issuer price of the syrupUSDC and ONyc token legs. In every case the loss of about 3,000 × 0.05 = 150 USDC on a 3,000 USDC leg is booked through the protocol's real price path, Junior takes all of it, and Senior stays at 4,000.

Advanced details

  • Locked profit absorbs a loss first. A vault can smooth profit by releasing each gain gradually over a profit unlock period that the curator sets. Gains that are not yet released are held as locked profit, and a loss consumes that amount before it reaches the tranches. The program's default period is 0, which turns smoothing off.
  • The incident test. A loss opens an incident when any of it reaches Senior, or when loss × 10,000 > threshold × booked, where threshold is the incident threshold in basis points and booked is Senior's value plus Junior's value. With the default threshold of 25 and 5,000 USDC booked, that is any loss above 25 × 5,000 ÷ 10,000 = 12.50 USDC.
  • What "frozen" means exactly. In Recovery the program refuses funding (VaultFrozen) when Senior's impairment is above 0, or when Senior's value at the cautious low end is below its booked value.
  • The cost basis floor. A leg's cost basis is the USDC put into it minus the USDC taken out. A leg that drops and then reports its value again is recognised back up to its cost basis at once, without waiting for the growth cap that slows other rises. This is why the comeback in Example 5 is immediate.
  • The program accepts an incident threshold of up to 10,000 basis points, which is 100%.

On this page