용어집
같은 것을 가리키는 여러 이름을 하나로 정리한 페이지다. NAV 작업에서 세 가지 문제가 반복해서 나왔기 때문에 만들었다. 회의에서 나온 한국어 용어에 대응하는 코드 이름을 아무도 대지 못한 일, 산식 중간값에 이름 자체가 없던 일, 그리고 가장 나쁜 경우로 한 단어가 실제로 서로 다른 두 값을 가리킨 일이다.
산식을 쓰거나 컬럼 이름을 정하거나 투자자 문구를 쓸 때 이 페이지를 본다. 설계 문서가 아니라 조회표다. 모든 항목은 그 개념을 소유한 페이지를 가리킨다.
충돌하는 용어: 여기부터 정리한다
우리 문서 안에서 두 가지 이상을 뜻해 온 다섯 단어다. 각 행은 제안이 아니라 확정된 정리 결과다.
분모 (denominator)
세 개가 있고, 서로 다른 산식이며, 하나로 합치면 퇴행이다. 이 페이지에서 가장 영향이 큰 충돌이다. ⚠️ 수익 쪽 분모는 함정이 두 겹이다. 풀이 보유한 LP를 제외하는데, v3-104 안 A도 똑같이 제외했다. 하지만 안 A는 *"에스크로된 LP는 아무것도 못 번다"*였고 v3-131 (3)은 *"벌긴 버는데 장부 기록을 에스크로 종료 시점으로 미룬다"*는 뜻이다. 식은 같고 규칙은 반대다. 산식이 아니라 상태 열을 읽어야 한다.
| 맥락 | 분모 | 결정 | 상태 |
|---|---|---|---|
평시 NAV (nav_per_token) | totalSupply. pools.lp_total_supply(인덱서의 온체인 미러)에서 읽는다 | R9 | 반영 완료. nav-formula.ts:242가 이 값으로 나누고, 미러가 NULL이거나 0이면 usableTotalSupply가 폴백하지 않고 가격 매기기를 거부한다 |
| 수익 발생(인식된 채무) | totalLpSupply − poolHeldLp. 풀이 보유한 LP는 면제된 게 아니라 잠긴 것이고, 에스크로가 끝날 때 요청 단위로 몫이 기록된다 | v3-131 (3) | 2026-08-20 반영 완료. ⚠️ v3-104 안 A와 식은 같지만 규칙이 다르다. 안 A는 발생분을 없앴고 이건 기록을 미룬다. 미루는 부분이 previewEscrowAccrual(requestId)다 |
| Wind-down NAV | claimingSupply = totalSupply − settledUnclaimedLp. 정산됐지만 미청구인 것만 빼고, 대기 중이거나 이월된 LP는 남는다 | R10 | 반영 완료. 2026-08-04 배포이고 신규 풀만 해당된다(06 → R10 참조) |
누군가 "그 분모"라고 하면 어느 분모인지 되물어야 한다. R9 이후 total_deposited는 어디에서도 분모가 아니다. 분자의 원금 항으로만 남는다.
분자의 원금 항: 문서는 total_deposited, 코드는 pools.tvl
이 문서들의 모든 NAV 산식은 분자의 원금을 **total_deposited**로 쓴다. 코드는 pools.tvl로 나눈다(nav-formula.ts:66-68, nav-suggest.ts:89). 둘을 같은 항으로 읽되, total_deposited는 컬럼이 아니라 라벨로 읽어야 한다. pools에는 그런 컬럼이 없고, 실제로 있는 pool_chain_deployments.total_deposited에는 라이터가 아무 데도 없다.
이 구분은 표기 문제가 아니라 동작에 걸려 있다. pools.tvl은 상환 지급액을 뺀 순액이다(complete_redemption_atomic이 tvl = tvl − payout을 수행한다). 바로 이것이 R9를 출구에 대해 불변으로 만든다. T 토큰을 상환하면 분자는 V − T·p, 분모는 S − T가 되어 가격이 그대로다. "늘기만 하는" 누계값을 쓰면 나간 투자자의 원금이 분자에 남는 동안 그 토큰은 분모에서 빠져, 상환이 일어날 때마다 NAV가 부풀려진다. 그러므로 결정 카드에 total_deposited가 "늘기만 한다"고 적혀 있다면 라벨이 틀린 것이고 컬럼이 맞다.
🔴 2026-08-27 결정(v3-153) 아직 반영 안 됨. 배포된 엔진은 이렇게 동작하지 않는다. 결정 두 건이 이자 발생에서 "원금"이 무엇인지를 확정한다.
- 발생분은 NAV를 따라가지 않는다. 이자는 상각된 가치가 아니라 원금에 붙는다. 근거는 IFRS 9 §5.4.1(b)다. 손상은 대주의 이자수익 인식을 상각후원가로 줄이지만, 차주의 계약상 이자 의무는 그대로다. 이 시스템이 관리하는 것은 파트너의 채무 원장이므로 의무 쪽을 따른다.
- 그 원금은 납입 자본, 즉 들어온 돈에서 돌려준 돈을 뺀 값(
totalDeposited)이지 LP 토큰 수가 아니다. 배포된 엔진은 LP 수를 기준으로 발생시킨다.
옛 문서에는 totalSubscription이 네 번째로 등장한다. 뒤에 코드가 없는 v3-16 잔재이니 되살리지 않는다.
한 달 (month)
시간 기준이 세 개이고, 서로 다른 것을 재며, 하나로 합치면 퇴행이다(v3-124). "한 달이 28일인가 30일인가"에는 답이 하나로 정해지지 않는다. 아래 세 축이 서로 다른 메커니즘에 묶여 있고, 그중 "달"이라는 말을 써도 되는 축은 하나뿐이기 때문이다.
| 축 | 기준 | 표현 방법 | 코드 |
|---|---|---|---|
| 회차 상환 주기 | 고정 28일 / 84일 | "28일마다"라고 쓴다. "매월"이라고 쓰지 않는다 | epoch_duration_days. PoolCommonLib.epochFundingDateAt = 직전 펀딩일 + 그 일수 |
| 수익 분배 | 역월, 연 12회 | "매월" | computeNextYieldDue가 월말 클램프와 함께 addUtcMonths 사용(yield-schedule.ts) |
| 락업 · 만기 · 기간 표시 | 30일(1개월 = ×30) | "N개월" = N × 30일 | firstYieldIntervalDays(30 / 90). 투자자 PDP는 days / 30으로 렌더. 백엔드 CUSTOM은 months × 30 |
| 즉시 상환 | 주기 없음 | 요청 단위로 정산 | 단위 개념이 없음 |
정리하면 28은 딱 한 곳(회차 주기의 일수)에만 존재하고, "달"이라 부르는 값은 전부 30이다. 그래서 어드민 생성 화면의 주기 선택은 Every 28 days로 표기하며, 그 덕분에 "달"이라는 단어가 다른 모든 곳에서 하나의 뜻으로 쓰일 수 있다.
주기가 왜 고정 일수인가. 컨트랙트는 역법 연산을 전혀 하지 않고 epochDurationDays × 1 days만큼 전진한다. 28과 84는 7의 배수라, 파생되는 모든 경계(윈도우 개시, 마감, 펀딩일)가 영원히 같은 요일에 떨어진다. 이 덕분에 "윈도우는 월요일에 열린다"가 운영자가 계획을 세울 수 있는 사실이 된다. 07 → Model B 참조.
이 셋을 "통일"하지 말 것. 구체적인 퇴행 두 가지
firstYieldIntervalDays를 28로 낮추지 말 것. 이것은 "한 달"의 정의가 아니라, 락업이 풀리기 전에 풀의 첫 수익 분배가 이미 일어났음을 보장하는 하한이다. 그래야 YIELD_BASED 패널티가 뗄 대상이 생긴다. 이 값은 역월 축을 따르고 첫 분배는 30~31일 뒤에 떨어진다. 28로 두면 락업이 이틀 일찍 끝나 accrued_yield가 0인 상태가 되고, 조기 이탈에서 아무것도 몰수되지 않는다. v3-84가 막으려던 바로 그 구멍이다. pools.post.create.ts와 pools.patch.update.ts에서 강제하고 어드민 위저드도 같은 규칙을 따른다.
회차 주기를 30 / 90으로 바꾸지 말 것. 얻는 게 없고 잃는 게 셋이다. 30 mod 7 = 2, 90 mod 7 = 6이라 요일 안정성이 사라지고 윈도우가 주말로 밀리기 시작한다. 90은 MAX_EPOCH_DURATION_DAYS에 정확히 걸려 여유가 없다. setEpochSchedule은 생성 시점에만 호출되므로 이미 28로 배포된 풀은 그대로 남고 운영 자산이 둘로 갈린다. 게다가 수익은 여전히 역월을 따르므로 집합이 균일해지지도 않으면서 v3-93만 뒤집는 셈이 된다.
완전한 통일은 선택지에 없다. 그러려면 수익 축도 고정 일수가 돼야 하는데, 그러면 연 13회 지급이 되고 지급일이 매 기간 밀린다. 월말 NAV, 명세서, 회계 주기가 이를 감당하지 못한다. DPD는 어느 쪽으로도 걸림돌이 아니다. 이미 일 단위로 센다(30 / 60 / 90 DPD). 오히려 일수 기준을 지지하는 근거다.
완충자금 (buffer)
두 개의 자금이고 역할이 정반대다. 단어만 쓰면 모호하니 매번 수식어를 붙인다.
| 용어 | 뜻 | 손실 흡수? |
|---|---|---|
| buffer / 파트너 완충자금 / manager equity buffer | 펀드매니저 자신의 first-loss 자본(R6). buffer_rate_bps / buffer_direction / buffer_basis가 계산 가능한 형태이고, equity_buffer_rule은 그 산문 고지다 | 한다. 단독 풀의 first-loss가 이것이다 |
| reserve / 리저브 | reserve_bps 몫을 외부 reserve_wallet으로 라우팅 | 안 한다(R8). 상환 유동성 전용이다. 이미 가격에 반영된 청구권 안에 들어 있는 투자자 돈이다 |
옛 문서는 reserve를 "first-loss 버퍼"라고 불렀고, 그것이 R8이 제거한 이중 계상을 만들었다. 매니저 자본을 뜻할 땐 buffer, 남겨둔 입금액을 뜻할 땐 reserve라고 쓴다. "reserve buffer"라는 말은 절대 쓰지 않는다.
비율 하나만으로는 이 계층을 설명하지 못한다(마이그레이션 0118). 컬럼 셋이 필요하고, 그중 둘은 비율이 결정할 수 없는 것을 결정한다.
| 컬럼 | 값 | 무엇을 정하나 |
|---|---|---|
buffer_rate_bps | 0~10000. 현재 모든 풀에서 0 | 계층의 크기. 0이면 이 항이 사라진다 |
buffer_direction | FIRST_LOSS(기본) / EXCESS | 누가 손실을 지는가. 한도 5%, 손실 8%일 때 FIRST_LOSS면 투자자가 3%, EXCESS면 5%를 진다 |
buffer_basis | GROSS(기본) / NET | 보고된 cumulative_loss가 파트너 흡수 전 값인지 이미 흡수된 뒤 값인지. NET이면 한도를 0으로 강제한다. 두 번 빼는 것은 한 계층 위의 R8이다 |
equity_buffer_rule은 이들과 병렬이 아니라 종속이다. 0121이 buffer_rate_bps > 0이 아니면 산문 입력을 거부하므로, 흡수하는 게 아무것도 없는 풀에 "Manager first-loss commitment"라는 투자자 문구가 뜰 수 없다.
손실 (loss): 아무도 명시하지 않았던 통화
리뷰에서 나온 가장 비싼 충돌이다. 양쪽 다 그냥 숫자로 읽히기 때문이다. 파트너는 자기 통화로 보고하고(Joob은 EFIDR), 풀의 장부는 USD다.
| 용어 | 통화 | 비고 |
|---|---|---|
cumulative_loss, total_subscribed, total_outstanding_principal | 펀드 쪽 | external_pool_data_snapshots로 들어오는 모든 것 |
total_deposited, uncoveredLoss, buffer | 풀 쪽 | NAV 산식 안의 모든 것 |
허용된 교차는 환율이 아니라 비율뿐이다(R7). lossRatioFromFundReport() = cumulative_loss ÷ total_subscribed이고, 둘 다 같은 보고서에서 오므로 통화가 약분돼 무차원 값이 된다. 그 비율을 풀의 장부에 적용한다.
pools.fx_rate로 교차하지 말 것
환율 변환은 그럴듯한 숫자를 만들어 내면서 환율 변동을 그대로 NAV 안으로 집어넣는다. 실제로 장애를 만들었다. 달러에서 루피아를 빼면서 NAV가 −1782가 나왔고 하한으로 클램프됐다. dev에서 생성된 유일한 NAV 제안이, 대출이 전부 정상인 풀에 대한 전손 상각이었다. fx_rate는 표시용 변환에만 쓴다.
회차추가분 (epoch top-up)
두 이름 다 온체인에 실재하고 범위가 다르다.
| 이름 | 범위 |
|---|---|
epochFundTopUp[epochId] | 회차별 매핑. 파트너가 한 회차에 대해 넣은 금액 |
totalEpochTopUp | 미소진분 epochFundTopUp[*]의 합을 담은 스칼라 미러. 온체인에서는 매핑을 순회할 수 없기 때문에 둔다 |
wind-down 분자는 totalEpochTopUp을 쓰고, 정산 시 소진 순서는 epochFundTopUp[id]부터 끌어다 쓴다. 서로 바꿔 쓸 수 없다.
정산 NAV (settlement NAV)
| 이름 | 위치 |
|---|---|
settleNav | 온체인 로컬 변수이자 EpochSettled 이벤트의 세 번째 인자(전체 다섯 개 중) |
settled_nav | 그 값의 DB 미러 |
같은 숫자를 레이어에 따라 다르게 적은 것이다. nav_at_request(즉시 풀)나 navPerToken(현재가)과는 다르다.
스케일 축: 통화가 아니라 소수 자릿수
숫자 스케일 세 개가 동시에 살아 있고, 위의 통화 문제와는 직교한다. 모든 금액은 정확히 하나의 축 위에 있다.
| 축 | 소수 자릿수 | 사용처 | 변환 함수 |
|---|---|---|---|
Raw (RawAmount) | 스테이블코인 자체 자릿수(USDC = 6) | 실제로 전송되는 모든 것 | toRaw / fromRaw |
Normalized (NormalizedAmount) | 18 | 풀 내부 원장 카운터 | toNormalized / fromNormalized / fromChainNormalized |
NAV 가격 (NavPrice) | 6 (NAV_PRECISION = 1e6) | 토큰당 가격 전용 | toNavPrice / fromNavPrice |
이들은 브랜드 TypeScript 타입(apps/infra/lib/shared/contract/units.ts)이고, 빌드 가드(scripts/check-onchain-units.mjs)가 그 모듈 밖의 캐스팅을 거부한다. 캐스팅은 축을 단정하는 행위이고, 축을 바꾸는 것은 변환 함수뿐이다.
브랜드가 막아 주는 실수
reserveBalance()는 normalized 카운터를 반환한다. 인덱서가 이를 raw로 착각해 1e6으로 나누는 바람에, $2.10을 들고 있는 풀이 $2,100,000,000,000으로 미러링됐다. 인덱서 자체가 죽어 있어서 몇 주 동안 아무도 몰랐다. v3-101과 같은 6 대 18 혼동인데 방향만 반대다.
유동성 버킷: 설계상 서로 겹치지 않는다
불변식은 PlatformPoolStorage에 있다.
physicalBalance >= reserveBalance + redemptionCommitted + unclaimedYield| 용어(한글) | 코드 이름 | 단위 | 뜻 |
|---|---|---|---|
| 리저브 · 상환 유동성 | reserve_wallet / reserveWallet | 외부 지갑 잔액 | 현재 소스의 외부 리저브 풀은 입금의 reserve_bps 몫을 reserve_wallet으로 보내고 나머지를 fund_wallet으로 보낸다. 풀 내부 리저브 잔고와 getter는 제거됐다. 리저브 비율은 보유 비율이 아니라 외부 지갑으로 보내는 비율이다. 외부 잔액이 풀의 지급 재원으로 자동 보충되지는 않는다. |
| 회차 추가분 | epochFundTopUp[id] / totalEpochTopUp | Normalized | 위 회차추가분 참조 |
| 정산 예약분 | redemptionCommitted | Normalized | 정산됐지만 미청구인 상환 채무. USD 단위이며 claim은 여기서만 끌어간다 |
| 정산 미청구 LP | settledUnclaimedLp | LP 토큰 | 위 항목의 토큰 쌍둥이. R10에서 추가 |
| 미분배 수익 | unclaimedYield | Normalized | ⚠️ 이름이 오해를 부른다. 입금됐지만 아직 분배되지 않은 수익이다. depositYield가 크레딧하고(YieldLib.sol:122) settleYield가 차감한다(:179). "분배됐는데 아무도 청구 안 한" 것이 아니다. 그건 pendingYield[investor]라는 별개 채무다(아래 참조) |
네 항목 형태가 빠뜨리는 다섯 번째 채무가 있다
SUM(pendingYield[investor]), 즉 홀더에게 이미 분배됐고 아직 청구되지 않은 수익이다. unclaimedYield는 이걸 포함하지 않는다(분배 시점에 차감됐다). 그런데 돈은 풀 안에 있고 갚아야 할 몫이다. Foundry 불변식 invariant_backingCoversObligations는 다섯 개를 다 세고, 위의 스토리지 주석은 넷만 센다. 네 항목 형태로 "가용 잔액"을 계산하는 코드는 전부 과대 계상하고 있다. 23-money-path §6의 클램프 주의 항목 참조.
버킷은 이미 redemptionCommitted를 뺀 값이다
_reserveFilledGross가 정산된 총액을 유동성 버킷 셋에서 차감해 redemptionCommitted로 옮겨 둔다. 여기서 다시 빼면 같은 돈을 두 번 제거하는 것이다. executeWindDown이 실제로 그렇게 해서, 미청구 유동성 $50을 들고 있는 풀에 max(0, 50 − 100) = 0을 반환했다.
상환 용어
| 용어(한글) | 코드 이름 | 뜻 |
|---|---|---|
| 즉시 상환 | epoch_duration_days = 0 | 요청 단위로 가격을 매기고 정산 |
| 회차 상환 | epoch_duration_days > 0 | 묶어서 pro-rata, forward pricing, pull claim |
| 회차 | currentEpochId | 정산 주기 1회 |
| 이월 수요 | epochCarryDemandLp | 못 채워 넘어온 수요. 신규 수요보다 먼저 처리 |
| 신규 수요 | epochNewDemandLp | 이번 주기에 들어온 수요 |
| 체결 비율 | epochNewFillRatio / carry ladder | 한 티어의 수요 중 체결된 비율 |
| 락업 | lockup_days | 상환이 완전히 차단됨. 패널티 타입과 독립 |
| 조기 이탈 | EARLY | 락업은 끝났고 만기는 안 왔음. 패널티가 붙을 수 있음 |
| 패널티 | penalty_rate_bps | 원금에서 차감해 fund_wallet으로 보냄 |
원장 용어
두 단어가 자금 흐름 아키텍처의 대부분을 지고 있는데, 둘 다 이름만으로는 뜻이 드러나지 않는다. 전체 모델은 23-money-path §9에 있다.
fold
함수형 프로그래밍의 reduce다. 모든 이벤트를 순서대로 적용해 값 하나를 만든다. foldLedger(events, config)가 money_events를 잔액으로 바꾼다.
이 단어는 증분(increment)의 반대라는 뜻으로 쓰이고, 그 차이 위에 기록 모델 전체가 서 있다.
| increment | fold | |
|---|---|---|
| 쓰기 | tvl = tvl + 100 | tvl = f(모든 이벤트) |
| 두 번 적용하면 | 200. 틀린 값 | 같은 답 |
| 무엇이 참인가 | 그 숫자 | 이벤트들. 숫자는 그걸 요약한 것 |
| 규칙이 틀렸을 때 | 되돌릴 수 없다. 입력이 사라졌다 | 규칙을 고치고 다시 fold하면 이력이 교정된다 |
| 검증 가능한가 | 불가능. 비교 대상이 없다 | 가능. 다시 fold해서 비교한다 |
fold한 값은 저장한다(money_pool_state, portfolio_positions). 읽을 때마다 재계산하면 느리기 때문이다. 요점은 저장된 값이 조정이 아니라 재구축이라는 것이다. 버리고 다시 만들 수 있다. scripts/money/replay.ts가 정확히 그 일을 하고 차이를 보고한다. 차이가 있다면 fold에 규칙이 빠졌거나, fold가 아닌 무언가가 그 컬럼에 썼다는 뜻이다.
fast path
원장에 쓰는 두 경로 중 즉시 반응하는 쪽이다. 다른 하나인 sweep과 대비되는 이름이다.
| 트리거 | 읽는 것 | |
|---|---|---|
| fast path | 트랜잭션 해시를 이미 아는 핸들러 | 그 영수증 하나를 즉시 |
| sweep | EventBridge, 약 2분 주기 | 블록 범위에 대한 eth_getLogs |
투자자가 입금하면 브라우저가 트랜잭션 해시를 보낸다. 그 시점에 어느 트랜잭션인지 이미 알고 있으니 sweep이 찾아 줄 때까지 기다릴 이유가 없다.
중복은 의도된 것이다. fast path가 실패해도 호출자에게 보고하는 오류가 아니다. 돈은 어차피 온체인에 있고 sweep이 주워 간다. 확정된 트랜잭션에 "입금 실패"라고 답하는 게 지연보다 나쁘다. 두 경로는 서로가 뭘 했는지 알 필요가 없다. 무엇이 새 것인지는 UNIQUE (chain_id, tx_hash, log_index)가 결정한다.
비율은 basis point다
저장·전송·계산 모두 bps로 한다. 1% = 100 bps, 100% = 10000 bps. reserve_bps, redemption_gating_bps, penalty_rate_bps, buffer_rate_bps, net_yield_fee_config.*_bps가 해당된다.
percent는 정확히 두 곳에만 있고 둘 다 단방향이다. 어드민 입력(%로 타이핑하고 저장 시 한 번 변환)과 표시(formatBpsAsPercent)다. percent로 저장하거나 전송하지 않는다.
⚠️ apy_rate가 예외다. 이건 percent 숫자라서 perf_hurdle_bps와 비교하려면 apy_rate × 100이 필요하다.
🔴 2026-08-27 결정(v3-154) 아직 반영 안 됨. 마이그레이션이 없고 apy_rate는 여전히 NOT NULL이다. 두 rate 필드가 발생 모드에 따라 배타적이 된다. FIXED 풀은 accrual_rate_bps만 갖고 apy_rate는 비운다. TARGET 풀은 apy_rate만 갖는다. 따라서 위 문장은 FIXED 풀에는 더 이상 성립하지 않는다. 거기서 의미 있는 rate는 accrual_rate_bps이고 이미 bps라 × 100을 하면 안 된다. hurdle 비교에 빈 apy_rate를 넣으면 조용히 0과 비교하게 된다.
한글 ↔ 공식 이름 (NAV 리뷰에서 쓴 기호)
NAV 작업 문서의 약칭을, 코드와 스키마와 이 문서들에 등장하는 이름에 대응시킨 표다. 오래 남을 문서에는 오른쪽 열을 쓴다. 기호는 화이트보드용이다.
| 기호 | 한글 / 뜻 | 공식 이름 |
|---|---|---|
nav · S | 토큰 1개 가격 · 토큰 총량(상환 시 감소) | nav_per_token · totalSupply |
Dep | 총 예치액. 분자의 원금 항이고 더 이상 분모가 아니다(R9) | total_deposited |
R · R_avail | reserve 잔액 · 라이브 온체인 reserve. 유동성이지 손실 흡수가 아니다 | reserveBalance / pools.reserve_balance |
A · T | 예치 금액 · 토큰 수 | amount · tokens |
loss_cum · U_cum | 실현 손실 누계(write-off 원금) · 흡수층이 못 막은 누적 손실 | cumulative_loss · uncoveredLoss |
B | first-loss buffer 한도. 매니저의 equity 버퍼(R6)다. 줄어드는 잔액이 아니라 상한이다 | buffer_rate_bps에서 계산한 bufferCap(equity_buffer_rule이 그 산문 형태) |
E_b · ρ_b | 버킷 노출 · 상각률. 보류된 자동화 템플릿 전용(v3-13 OJK 래더)이고 현행 정책이 아니다 | exposure · rate |
C | reserve 흡수분. R8에서 폐기됐고 항상 0이다(NO_RESERVE_CONSUMED) | reserveConsumed / nav_history.reserve_consumed |
U · 첨자 ⱼ | NAV로 넘어간 손실 · Junior 값 | uncovered · Junior 트랜치 |
f · A_e · D_e | epoch 체결 비율 · 지급 가능 자금 · 요청 토큰 총량 | fillRatio · available · demand |
nav_req / nav_set / nav_wd | 요청 / 정산 / 청산 시점 NAV | nav_at_request · settleNav/settled_nav · wind-down navPerToken |
pen | 페널티. fund_wallet으로 가고 reserve로는 절대 가지 않는다(v3-85) | penalty_amount |
fx(t) | 환율. NAV 입력이 아니다(R7). 상환 시점에 적용한다 | fx_rate / fx_rate_source |
FV · RNI · NPL · OUT | 펀드 총가치 · 실현 순수익 · 연체 잔액 · 미상환 원금 | 파트너 템플릿 필드 → external_pool_data_snapshots |
파트너 피드가 만족해야 하는 데이터 불변식: FV = Dep + RNI − NPL, 그리고 utilization = OUT / Dep.
이름이 없던 중간값들
산식과 대화에는 등장하는데 이름이 없던 값들이다. 그래서 같은 양이 두 가지 방식으로 계산되곤 했다. 이제 이름을 고정한다.
| 이름 | 정의 | 비고 |
|---|---|---|
bufferAbsorbed | min(cumulativeLoss, bufferCap). 매니저 버퍼가 흡수한 누적 손실 | ⚠️ 표시 전용. 산식에 쓰기 전에 아래 경고를 볼 것 |
netYield | gross − 플랫폼 몫 − SPC mgmt − pool mgmt − perf fee − WHT. 지분당 실제로 크레딧되는 금액 | settleYield의 net 인자가 나르는 값이다. depositYield가 받는 gross와 다르다 |
nplBalance | npl_threshold_days 이상 구간에 있으면서 아직 상각되지 않은 노출 | 장부에 남아 있는 미실현분이다. npl_ratio에 들어가고 NAV는 절대 건드리지 않는다(R5.5) |
depositCum | 누적 입금. Dep / total_deposited와 같은 값 | "누적 입금"이 별개의 두 번째 숫자로 읽히지 않게 하려고 이름만 붙였다 |
settledUnclaimedLp | 정산은 됐지만 청구되지 않은 지급의 뒷받침 LP. 정산 시 +, claim/burn 시 − | R10 카운터다. 2026-08-04 배포(커밋 0abe168, 신규 팩토리)이며 신규 풀만 해당된다. PlatformPoolStorage.sol:227에 있고 동명 getter로 노출되는데, 배포 이전 클론에서는 revert한다 |
bufferCap | total_deposited × buffer_rate_bps / 10000. buffer_basis = NET이면 0으로 강제 | rate가 0이면 cap도 0이고 버퍼가 없다는 뜻이다. rate가 확정되기 전까지 모든 풀이 여기 해당된다 |
claimingSupply | totalSupply − settledUnclaimedLp | wind-down 분모 전용. 평시 NAV 분모로 쓰지 않는다 |
lossRatio | cumulative_loss ÷ total_subscribed. 둘 다 같은 파트너 보고서에서 온다 | 구조상 무차원이다. 손실 참조 |
distributable | reserveBalance + totalEpochTopUp | 한 회차 정산이 실제로 지급할 수 있는 금액. 이미 redemptionCommitted를 뺀 값이다 |
announcedNav | hasPendingNavUpdate ? pendingNav : navPerToken | PoolCommonLib.effectiveNav(). 24시간 예고 기간에 입금(PlatformPool.sol:756), 즉시 풀의 reserve 게이트와 요청 스냅샷(RedemptionLib.sol:279, :327), 회차 정산(:762), 재투자(YieldLib.sol:299)가 인용하는 가격이다. 재투자는 2026-08-06 구현체부터, 나머지는 2026-08-04부터이고, Clones 풀은 태어날 때의 구현체를 계속 쓴다(R2·R3) |
bufferBalance를 줄어드는 카운터로 두면 이중 차감이 된다
쓰고 싶어지는 상태형(bufferBalance = bufferCap − bufferAbsorbed, 그다음 uncovered = loss − bufferBalance)은 흡수분을 두 번 뺀다. loss = cap = 100일 때 정답은 0인데 uncovered로 100을 반환한다. 산식은 절대값 기준이라 버퍼에는 상태가 전혀 필요 없다. FIRST_LOSS면 uncovered = max(0, loss − cap), EXCESS면 min(loss, cap)이다. bufferAbsorbed는 계층이 얼마나 소진됐는지 운영자에게 보여주려고 존재하고, 회귀 테스트가 잘못된 형태를 고정해 막는다.
NPL과 write-off: 같은 1달러가 둘 다일 수는 없다
가장 흔한 데이터 반입 오류라서 06뿐 아니라 여기에도 적는다.
- NPL(
npl_threshold_days, 기본 90): 대출이 부실이지만 아직 장부에 남아 있다. 미실현이다. 미상환 원금에 포함되고 리스크 배지에 반영되며 NAV는 건드리지 않는다. - Write-off(
write_off_policy, 동남아 P2P는 보통 180 DPD 안팎): 펀드가 장부에서 대출을 제거한다. 실현 손실(cumulative_loss)이고, NAV를 움직이는 것은 이것뿐이다.
Aset은 펀드가 보고한 것을 미러링할 뿐, 자체 대손 곡선을 만들지 않는다(R5).
연체 쪽에도 고유한 이름들이 있고, 전부 NAV 입력이 아니라 신호다.
| 용어(한글) | 코드 이름 | 단위 | 뜻 |
|---|---|---|---|
| 연체일수 | dpd_days | 일 | 대출의 연체 경과일 |
| 연체 구간 | external_pool_dpd_buckets.lower_days | 일 | 구간 하한. 고정된 30/60/90이 아니라 동적 배열이다 |
| NPL 임계 | pools.npl_threshold_days | 일 | 이 구간 이상이면 부실로 본다. 기본 90 |
| NPL 비율 | npl_ratio | 비율 | 부실 노출 ÷ 미상환 원금 |
| 미상환 잔액 | total_outstanding_principal | 펀드 통화 | NPL 분모 |
상태 라벨: 만들어졌다 ≠ 돌아간다
리뷰에서 같은 질문("이 메커니즘 진짜 도는 건가?")이 계속 반복돼서 라벨을 하나 만들었다.
| 라벨 | 뜻 | 뜻하지 않는 것 |
|---|---|---|
| 휴면 레버(dormant lever) | 스위치가 온체인에 있고 테스트도 됐고 동작한다. 다만 어떤 제품 화면도 그걸 당기지 않아서 그 아래 동작이 라이브 풀에서 한 번도 실행되지 않는다 | 제거됨(코드는 있다) · 고장(동작한다) · 자동(누군가 당겨야 한다) |
| 폐기/제거됨 | 코드가 없어졌다. 아래 참조 | — |
| 🟡 배포 대기 | 작성되고 머지됐으나 아직 온체인에 없음 | 미구현 |
휴면 레버는 컨트랙트의 능력으로 읽고, 라이브 동작으로 읽지 않는다. 그 카운터는 영원히 0이므로, 해당 항이 들어간 산식은 사실상 그 항을 뺀 산식과 같다.
현재 살아 있는 사례는 없다. 이 라벨은 잔여분 hold-back(fundingRestricted / heldFundReleases)을 위해 만들어졌다. 온체인에 완전히 구현돼 퍼즈 테스트까지 마쳤고 의존 화면도 다 만들어진 채 놀고 있었는데, 배선되지 않고 0183에서 제거됐다(v3-112). 그래도 라벨은 남긴다. 같은 상황이 반복되기 때문이다. 기능은 그 기능을 쓰겠다는 결정보다 오래 살아남을 수 있고, 산식이 그게 돌아가는 것처럼 읽히게 두는 것보다 사실대로 적는 편이 정직하다.
폐기된 용어
옛 문서와 코드베이스 이력에 남아 있는 것들이다. 각각 올바른 대체어가 정확히 하나씩 있다.
| 용어 | 상태 |
|---|---|
보류 자금 / hold-back (heldFundReleases, fundingRestricted) | 0183에서 제거(v3-112). 파트너 잔여분은 입금 시점에 항상 fund_wallet으로 전송되고, reserveBalance가 풀의 유일한 유동성 버킷이다 |
pool_type (AS_POOL / FUND_POOL) | 제거됨(v3-01). fund_id로 분기한다 |
lp_issuance_model / FUND_ISSUED | 제거됨(v3-02). LP는 항상 플랫폼 발행이다 |
DISTRESSED | 제거됨. ACTIVE + writedown이나 IMPAIRED를 쓴다 |
| reserve를 "first-loss"라 부르던 것 | 폐기(R8). 상환 유동성 전용이다 |
reserve_consumed | 항상 0이다. 컨트랙트가 0이 아닌 값에 revert한다 |
NAV 분모로서의 totalSubscription | v3-16 잔재이고 뒤에 코드가 없다 |
recovery_flag | 추가됐다가 이틀 만에 빠졌다. NAV 상승은 승인 버튼만 필요할 뿐 다른 건 필요 없다 |
| Escrow 컨트랙트 · Receipt NFT · D+7 환불 | v3.0에서 제거 |
함께 보기: Writedown & NAV(이 용어들이 설명하는 모델) · 핵심 개념 · Money Path(지갑 네이밍) · v3-109(이 페이지가 반영한 결정들).