Skip to content

Glossary

Names for the same thing, reconciled. This page exists because the NAV work surfaced three recurring failures: a Korean term in a meeting whose code name nobody could produce, an intermediate value in a formula that had no name at all, and — worst — one word used for two genuinely different quantities.

Use this page when writing a formula, a column name, or investor copy. It is a lookup table, not a design document: every entry points at the page that owns the concept.

⚠️ Terms that collide — resolve these first

Five words that have meant more than one thing in our own documents. Each row is the resolution, not a suggestion.

분모 / "denominator"

There are three, they are different formulas, and unifying them is a regression. This is the single most consequential collision on this page. ⚠️ And the yield one is a trap twice over: it excludes pool-held LP, which is what v3-104 Option A also did — but Option A meant "escrowed LP earns nothing" while v3-131 (3) means "it earns, and the booking is deferred to the escrow's boundary". Same expression, opposite rule. Read the Status column, not the formula.

ContextDenominatorDecisionStatus
Ordinary NAV (nav_per_token)totalSupply, read from pools.lp_total_supply (the indexer's on-chain mirror)R9✅ shipped — nav-formula.ts:242 divides by it, and usableTotalSupply declines to price the pool when the mirror is NULL or 0 rather than falling back
Yield accrual (recognised debt)totalLpSupply − poolHeldLp — pool-held LP is parked, not waived: its entitlement is booked per request when the escrow endsv3-131 (3)✅ shipped 2026-08-20. ⚠️ Same expression as v3-104 Option A but a different rule — Option A destroyed the accrual, this defers booking it. previewEscrowAccrual(requestId) is the deferred part
Wind-down NAVclaimingSupply = totalSupply − settledUnclaimedLpsettled-but-unclaimed only, pending/rolled-over LP stays inR10✅ shipped — deployed 2026-08-04, new pools only (see 06 → R10)

If someone says "the denominator," ask which one. total_deposited is not a denominator anywhere after R9 — it survives only as the numerator's principal term.

The numerator's principal term: docs say total_deposited, code reads pools.tvl

Every NAV formula on these pages writes the numerator's principal as total_deposited. The code divides pools.tvl (nav-formula.ts:66-68, nav-suggest.ts:89). Read them as the same term, and read total_deposited as a label, not a columnpools has no such column, and the one that exists (pool_chain_deployments.total_deposited) has no writer anywhere.

The distinction is load-bearing, not cosmetic: pools.tvl is NET of redemption payouts (complete_redemption_atomic does tvl = tvl − payout), which is exactly what makes R9 invariant under an exit. A redemption of T tokens takes the numerator to V − T·p and the denominator to S − T, so the price is unchanged. A cumulative figure that "only grows" would keep the exited investor's principal in the numerator while their tokens left the denominator, inflating NAV on every exit. So where a decision card says total_deposited "only grows", the label is wrong and the column is right.

🔴 Decided 2026-08-27 (v3-153) (not yet reflected — the deployed engine does not work this way). Two decisions fix what "principal" means for interest accrual:

  1. Accrual does not track NAV — interest is charged on principal, not on the written-down value. The basis in IFRS 9 §5.4.1(b) is that impairment reduces the lender's interest revenue recognition to amortised cost, while the borrower's contractual interest obligation is unchanged. What this system keeps is the partner's liability ledger, so it follows the obligation.
  2. That principal is paid-in capital — money in minus money returned (totalDeposited) — not the LP token count. The deployed engine accrues off the LP count.

totalSubscription turns up in older text as a fourth. It is a v3-16 leftover with no code behind it; do not revive it.

한 달 / "month"

There are three time bases, they measure different things, and unifying them is a regression (v3-124). The question "is one month 28 days or 30?" has no single answer because the three axes below are anchored to three different mechanisms, only one of which the word "month" should be used for at all.

AxisBaseHow to say itCode
Epoch redemption cyclefixed 28 / 84 days"every 28 days" — never "monthly"epoch_duration_days; PoolCommonLib.epochFundingDateAt = previous funding date + that many days
Yield distributioncalendar month, 12 a year"monthly"computeNextYieldDue uses addUtcMonths with an end-of-month clamp (yield-schedule.ts)
Lock-up · maturity · duration display30 days (1 month = ×30)"N months" = N × 30 daysfirstYieldIntervalDays (30 / 90); investor PDP renders days / 30; backend CUSTOM months × 30
Instant redemptionno cyclesettles per requestunit is not involved

So: 28 exists in exactly one place (the epoch cycle, as a day count), and every value called a "month" is 30. The cadence selector in admin create therefore reads Every 28 days, which is what frees the word "month" to mean one thing everywhere else.

Why the cycle is a fixed day count. The contract does no calendar arithmetic at all — it advances by epochDurationDays × 1 days. 28 and 84 are multiples of 7, so every derived boundary (window open, cutoff, funding date) keeps the same weekday forever, which is what makes "the window opens on a Monday" something an operator can plan around. See 07 → Model B.

Do not "unify" these — two specific regressions

Do not lower firstYieldIntervalDays to 28. It is not a definition of "month"; it is the floor that guarantees a pool's first yield distribution has already happened before a lock-up lifts, which is what gives a YIELD_BASED penalty something to take. It tracks the calendar-month axis, where the first distribution lands 30-31 days out. At 28 the lock-up would end two days early with accrued_yield still at 0, so an early exit forfeits nothing — the exact hole v3-84 was created to close. Enforced in pools.post.create.ts and pools.patch.update.ts, mirrored in the admin wizard.

Do not change the epoch cadence to 30 / 90. It buys nothing and costs three things: 30 mod 7 = 2 and 90 mod 7 = 6, so weekday stability is gone and windows start drifting onto weekends; 90 sits exactly on MAX_EPOCH_DURATION_DAYS, leaving no headroom; and setEpochSchedule is create-only, so pools already deployed at 28 keep it and the estate splits. It would also reverse v3-93 without making the set uniform anyway, since yield would still be on the calendar.

Full unification is not available. It would require the yield axis to become a fixed day count too — 13 payouts a year on a date that drifts every period, which month-end NAV, statements and accounting cycles cannot absorb. DPD is not a blocker either way: it is already counted in days (30 / 60 / 90 DPD), which argues for day counts rather than against them.

완충자금 / "buffer"

Two funds, opposite roles. The word alone is ambiguous, so qualify it every time.

TermMeansAbsorbs loss?
buffer / 파트너 완충자금 / manager equity bufferThe fund manager's own first-loss capital (R6). buffer_rate_bps / buffer_direction / buffer_basis are the computable form; equity_buffer_rule is its prose disclosureyes — this is first-loss for standalone pools
reserve / 리저브reserve_bps share routed to external reserve_walletno (R8) — redemption liquidity only. It is investor money already inside the priced claim

The old docs called the reserve a "first-loss buffer," which is what produced the double-count R8 removed. When you mean the manager's capital, say buffer; when you mean the deposit share routed to the reserve wallet, say reserve. Never "reserve buffer."

A rate alone does not describe the layer (migration 0118). Three columns do, and two of them decide things the rate cannot:

ColumnValuesWhat it settles
buffer_rate_bps0–10000, 0 on every pool todayThe size of the layer. At 0 the term vanishes
buffer_directionFIRST_LOSS (default) / EXCESSWho takes the loss. 5% cap, 8% loss: investors take 3% under FIRST_LOSS, 5% under EXCESS
buffer_basisGROSS (default) / NETWhether the reported cumulative_loss is before the partner's absorption or already after it. NET forces the cap to 0 — deducting twice is R8 one layer up

equity_buffer_rule is subordinate to them, not parallel: 0121 refuses the prose unless buffer_rate_bps > 0, so the investor line "Manager first-loss commitment" cannot appear on a pool where nothing absorbs.

손실 / "loss" — the currency nobody stated

The costliest collision of the review, because both sides read as plain numbers. The partner reports in its own currency (Joob: EFIDR); the pool's book is USD.

TermCurrencyNote
cumulative_loss, total_subscribed, total_outstanding_principalThe fund'sEverything arriving in external_pool_data_snapshots
total_deposited, uncoveredLoss, bufferThe pool'sEverything inside the NAV formula

The only sanctioned crossing is a ratio, never an exchange rate (R7): lossRatioFromFundReport() = cumulative_loss ÷ total_subscribed, both from the same report, so the currency cancels and the result is dimensionless. Apply that to the pool's book.

Never cross it with pools.fx_rate

An FX conversion produces a believable number and puts a currency move straight back inside NAV. It also produced the live defect: rupiah subtracted from dollars returned NAV −1782, clamped to the floor, and the only NAV proposal dev ever generated was a total-loss write-down on a pool whose loans were all performing. fx_rate is for display conversion only.

회차추가분 / epoch top-up

Both names are real on-chain and mean different scopes:

NameScope
epochFundTopUp[epochId]Per-cycle mapping — what the partner funded for one epoch
totalEpochTopUpScalar mirror of the sum of unconsumed epochFundTopUp[*], because a mapping cannot be iterated on-chain

The wind-down numerator uses totalEpochTopUp; the settlement spend order draws epochFundTopUp[id] first. Not interchangeable.

정산 NAV / settlement NAV

NameWhere
settleNavOn-chain local + the EpochSettled event's third argument (of five)
settled_navThe DB mirror of that value

Same number, two spellings by layer. Neither is nav_at_request (instant pools) or navPerToken (the live price).

Scale axes — decimals, not currency

Three numeric scales are live at once, and they are orthogonal to the currency question above. Every amount is on exactly one.

AxisDecimalsWhereConvert with
Raw (RawAmount)The stablecoin's own (USDC = 6)Anything that physically transferstoRaw / fromRaw
Normalized (NormalizedAmount)18The pool's internal ledger counterstoNormalized / fromNormalized / fromChainNormalized
NAV price (NavPrice)6 (NAV_PRECISION = 1e6)Per-token price onlytoNavPrice / fromNavPrice

These are branded TypeScript types (apps/infra/lib/shared/contract/units.ts) and a build guard (scripts/check-onchain-units.mjs) refuses casts outside that module. A cast asserts an axis; only a converter changes one.

The mistake the brands prevent

reserveBalance() returns a normalized counter. The indexer divided it by 1e6 as if it were raw, so a pool holding $2.10 mirrored as $2,100,000,000,000. It went unnoticed for weeks because the indexer was also dead. Same 6-vs-18 confusion as v3-101, opposite direction.

Liquidity buckets — disjoint by construction

The invariant lives in PlatformPoolStorage:

physicalBalance >= reserveBalance + redemptionCommitted + unclaimedYield
Term (KO)Code nameUnitMeaning
리저브 · 상환 유동성reserve_wallet / reserveWalletexternal wallet balanceIn the current external-reserve implementation, the reserve_bps share of each deposit goes to reserve_wallet and the remainder goes to fund_wallet. The in-pool reserve counter and getter are removed. The ratio specifies routing, not retention. An external balance does not automatically fund a pool payout.
회차 추가분epochFundTopUp[id] / totalEpochTopUpNormalizedSee epoch top-up above
정산 예약분redemptionCommittedNormalizedSettled-but-unclaimed redemption debt, in USD. Claims draw only from here
정산 미청구 LPsettledUnclaimedLpLP tokensThe token twin of the above. Added by R10
미분배 수익unclaimedYieldNormalized⚠️ Misleading name. Yield deposited but not yet distributeddepositYield credits it (YieldLib.sol:122), settleYield debits it (:179). It is not "distributed, nobody claimed"; that is pendingYield[investor], a separate obligation (see below)

There is a fifth obligation the four-term form leaves out

SUM(pendingYield[investor]) — yield already distributed to holders and not yet claimed. unclaimedYield does not cover it (it was debited at distribution), yet the money is in the pool and owed. The Foundry invariant invariant_backingCoversObligations counts all five; the storage comment above counts four. Anything computing "free balance" from the four-term form is overstating it — see the 🔴 clamp note in 23-money-path §6.

The buckets are already net of redemptionCommitted

_reserveFilledGross debits the settled gross out of the three liquidity buckets and parks it in redemptionCommitted. Subtracting it from their sum removes the same dollars twice — which executeWindDown did, returning max(0, 50 − 100) = 0 for a pool holding $50 of unclaimed liquidity.

Redemption vocabulary

Term (KO)Code nameMeaning
즉시 상환epoch_duration_days = 0Priced and settled per request
회차 상환epoch_duration_days > 0Batched, pro-rata, forward-priced, pull-claim
회차currentEpochIdOne settlement cycle
이월 수요epochCarryDemandLpUnfilled demand carried forward. Served before new demand
신규 수요epochNewDemandLpDemand from this cycle's vintage
체결 비율epochNewFillRatio / carry ladderShare of demand one tier filled
락업lockup_daysRedemption fully blocked. Independent of penalty type
조기 이탈EARLYLockup ended, maturity not reached. Penalty may apply
패널티penalty_rate_bpsDeducted from principal, paid to fund_wallet

Ledger vocabulary

Two words carry most of the money-flow architecture, and neither is self-explanatory. Full model: 23-money-path §9.

fold

reduce, in the functional-programming sense: apply every event in order to produce a value. foldLedger(events, config) turns money_events into a balance.

The word is doing work as the opposite of increment, and the difference is what the whole record model rests on:

incrementfold
The writetvl = tvl + 100tvl = f(every event)
Applied twice200 — wrongthe same answer
What is truethe numberthe events; the number summarises them
A wrong rulecannot be undone — the inputs are gonefix the rule, fold again, history corrects
Checkable?no; there is nothing to compare againstyes — re-fold and compare

The folded value IS stored (money_pool_state, portfolio_positions) because recomputing on every read would be slow. The point is that the stored value is a rebuild, never an adjustment, so it can be thrown away and regenerated. scripts/money/replay.ts does exactly that and reports differences; a difference means either the fold is missing a rule or something other than the fold wrote the column.

fast path

The prompt of the two write paths into the ledger. Named against the sweep, which is the other.

TriggerReads
fast patha handler that already knows the transaction hashthat one receipt, immediately
sweepEventBridge, ~2 mineth_getLogs over a block range

When an investor deposits, the browser posts the transaction hash — at that moment we already know which transaction it is, so there is no reason to wait for a sweep to find it.

The redundancy is deliberate. A failed fast path is not an error the caller reports: the money is on-chain either way and the sweep will pick it up, and answering "deposit failed" to a confirmed transaction would be worse than the delay. Neither path needs to know what the other did, because UNIQUE (chain_id, tx_hash, log_index) decides what is new.

Ratios are basis points

Stored, transmitted and computed in bps. 1% = 100 bps, 100% = 10000 bps. Covers reserve_bps, redemption_gating_bps, penalty_rate_bps, buffer_rate_bps and net_yield_fee_config.*_bps.

Percent exists in exactly two places, both one-way: admin input (typed as %, converted once on save) and display (formatBpsAsPercent). Never store or transmit percent.

⚠️ apy_rate is the exception — it is a percent number, so comparing it against perf_hurdle_bps needs apy_rate × 100.

🔴 Decided 2026-08-27 (v3-154) (not yet reflected — no migration yet, apy_rate is still NOT NULL): the two rate fields become exclusive by accrual mode. A FIXED pool carries only accrual_rate_bps and leaves apy_rate empty; a TARGET pool carries only apy_rate. The sentence above therefore stops holding for FIXED pools, where the rate that matters is accrual_rate_bpsalready bps, so no × 100. Feeding a hurdle comparison the empty apy_rate instead would silently compare against 0.

한글 ↔ 공식 이름 (symbols used in the NAV review)

The shorthand from the NAV working document, mapped to the names that appear in code, schema, and these docs. Use the right-hand column in anything durable — the symbols are for whiteboards.

SymbolKorean / meaningOfficial name
nav · S토큰 1개 가격 · 토큰 총량 (상환 시 감소)nav_per_token · totalSupply
Dep총 예치액 — the numerator's principal term, no longer a denominator (R9)total_deposited
R · R_availreserve 잔액 · 라이브 온체인 reserve — liquidity, not loss absorptionreserveBalance / pools.reserve_balance
A · T예치 금액 · 토큰 수amount · tokens
loss_cum · U_cum실현 손실 누계 (write-off 원금) · 흡수층이 못 막은 누적 손실cumulative_loss · uncoveredLoss
Bfirst-loss buffer 한도 — the manager's equity buffer (R6). A cap, not a depleting balancebufferCap from buffer_rate_bps (equity_buffer_rule = its prose form)
E_b · ρ_b버킷 노출 · 상각률 — deferred automation template only (v3-13 OJK ladder), not live policyexposure · rate
Creserve 흡수분 — retired by R8; always 0 (NO_RESERVE_CONSUMED)reserveConsumed / nav_history.reserve_consumed
U · 첨자 NAV로 넘어간 손실 · Junior 값uncovered · Junior tranche
f · A_e · D_eepoch 체결 비율 · 지급 가능 자금 · 요청 토큰 총량fillRatio · available · demand
nav_req / nav_set / nav_wd요청 / 정산 / 청산 시점 NAVnav_at_request · settleNav/settled_nav · wind-down navPerToken
pen페널티 — goes to fund_wallet, never the reserve (v3-85)penalty_amount
fx(t)환율 — not an input to NAV (R7); applied at redemptionfx_rate / fx_rate_source
FV · RNI · NPL · OUT펀드 총가치 · 실현 순수익 · 연체 잔액 · 미상환 원금partner template fields → external_pool_data_snapshots

Data invariant the partner feed must satisfy: FV = Dep + RNI − NPL, and utilization = OUT / Dep.

Intermediate values that had no name

These appear in formulas and in conversation but were never named, which is how the same quantity ended up computed two ways. Names are now fixed.

NameDefinitionNotes
bufferAbsorbedmin(cumulativeLoss, bufferCap) — cumulative loss the manager's buffer has taken⚠️ Display only. See the warning below before using it in a formula
netYieldgross − platform take − SPC mgmt − pool mgmt − perf fee − WHT — the amount actually credited per shareThe figure settleYield's net leg carries. Distinct from gross (what depositYield receives)
nplBalanceExposure in buckets at or past npl_threshold_days that is not yet written offOn-book, unrealized. Feeds npl_ratio; never moves NAV (R5.5)
depositCumCumulative deposits — the same figure as Dep / total_depositedNamed only to stop "cumulative deposits" reading as a second, different number
settledUnclaimedLpLP backing payouts that settled but were never claimed. + at settlement, at claim/burnThe R10 counter. Deployed 2026-08-04 (commit 0abe168, new factory), new pools onlyPlatformPoolStorage.sol:227, exposed by a same-named getter that reverts on a pre-deploy clone
bufferCaptotal_deposited × buffer_rate_bps / 10000, forced to 0 when buffer_basis = NETRate 0 ⇒ cap 0 ⇒ no buffer, which is every pool until a rate is confirmed
claimingSupplytotalSupply − settledUnclaimedLpWind-down denominator only. Never the ordinary NAV one
lossRatiocumulative_loss ÷ total_subscribed, both from the same partner reportDimensionless by construction — see 손실
distributablereserveBalance + totalEpochTopUpWhat an epoch settlement can actually pay. Already net of redemptionCommitted
announcedNavhasPendingNavUpdate ? pendingNav : navPerTokenPoolCommonLib.effectiveNav(). The price quoted during the 24h notice window by deposit (PlatformPool.sol:756), the instant reserve gate + request snapshot (RedemptionLib.sol:279, :327), epoch settlement (:762) and reinvest (YieldLib.sol:299) — reinvest from the 2026-08-06 implementation, the rest from 2026-08-04, and a Clones pool keeps whichever it was born with (R2·R3)

bufferBalance as a depleting counter is a double-count

The tempting stateful form — bufferBalance = bufferCap − bufferAbsorbed, then uncovered = loss − bufferBalance — subtracts the absorbed amount twice: at loss = cap = 100 it returns 100 uncovered when the correct answer is 0. The formula is absolute, so the buffer needs no state at all: uncovered = max(0, loss − cap) under FIRST_LOSS, min(loss, cap) under EXCESS. bufferAbsorbed exists to show an operator how much of the layer is used, and a regression test pins the wrong version out.

NPL vs write-off — one dollar is never both

The most common ingestion error, so it belongs here as well as in 06:

  • NPL (npl_threshold_days, default 90) — the loan is non-performing but still on the book. Unrealized. Counts toward outstanding principal, feeds the risk badge, does not touch NAV.
  • Write-off (write_off_policy, SEA P2P typically ~180 DPD) — the fund removes the loan from the book. Realized loss (cumulative_loss), and the only thing that moves NAV.

Aset mirrors what the fund reports and authors no provisioning curve of its own (R5).

The delinquency side has its own names, all of them signals rather than NAV inputs:

Term (KO)Code nameUnitMeaning
연체일수dpd_daysDaysDays past due on a loan
연체 구간external_pool_dpd_buckets.lower_daysDaysBucket lower bound. A dynamic array, not a fixed 30/60/90
NPL 임계pools.npl_threshold_daysDaysAt or past this bucket a loan is non-performing. Default 90
NPL 비율npl_ratioRatioNon-performing exposure ÷ outstanding principal
미상환 잔액total_outstanding_principalFund's currencyThe NPL denominator

Status labels — built ≠ live

One label, because we kept re-litigating the same question in review: is this mechanism real?

LabelMeansDoes not mean
Dormant leverThe switch exists on-chain, is tested, and works — but no product surface pulls it, so the behaviour below it never runs on a live poolRemoved (the code is there) · broken (it works) · automatic (someone must pull it)
Retired / removedThe code is gone. See below
🟡 Deploy-pendingWritten and merged, not yet on-chainUnbuilt

Read a dormant lever as contract capability, not as live behaviour. Its counters read 0 forever, so any formula containing one is effectively that formula minus the term.

There is no live instance today. The label was written for the remainder hold-back (fundingRestricted / heldFundReleases), which was fully implemented and fuzz-tested on-chain with every dependent surface built and idle — and was removed in 0183 rather than wired up (v3-112). It is kept as a label because the situation it names recurs: a capability can outlive the decision to use it, and the honest thing is to say so rather than let a formula imply it is running.

Retired terms

Present in older documents and in the codebase's history. Each one now has exactly one correct replacement.

TermStatus
보류 자금 / hold-back (heldFundReleases, fundingRestricted)Removed in 0183 (v3-112). The partner remainder always transfers to fund_wallet at deposit; reserveBalance is the pool's only liquidity bucket
pool_type (AS_POOL / FUND_POOL)Removed (v3-01). Branch on fund_id
lp_issuance_model / FUND_ISSUEDRemoved (v3-02). LP is always platform-issued
DISTRESSEDRemoved. Use ACTIVE + writedown, or IMPAIRED
Reserve as "first-loss"Retired (R8). Redemption liquidity only
reserve_consumedAlways 0. The contract reverts on any non-zero value
totalSubscription as a NAV denominatorv3-16 leftover, no code behind it
recovery_flagAdded and dropped inside two days. A NAV increase takes the approve button and nothing else
Escrow contract · Receipt NFT · D+7 refundRemoved in v3.0

See also: Writedown & NAV (the model these terms describe) · Core Concepts · Money Path (wallet naming) · v3-109 (the decisions this page reflects).