필드 거버넌스 매트릭스
한눈에 보는 내부 참조다. 거버넌스와 관련된 펀드·풀 필드마다 어디에 저장되는지(DB / 온체인), 누가 바꿀 수 있는지, 배포 후 수정 가능한지, 타임락이 얼마인지를 정리한다. 기존 출처를 모은 파생 뷰이고 새 규칙을 만들지 않는다.
기준 문서(값을 다른 곳에서 다시 적지 말 것): 수정 가능성 분류는 08 §온체인 값 동기화, 타임락 기간은 08 §timelock, money-path 불변성은 09a-custody §3, 컬럼 목록은 11-db-schema다.
2026-07-16 확인. §5는 2026-08-17에 상환 계획 컬럼(0189 / 0190, dev 적용 대기)으로 확장. 네이밍은 v3-70, 분류는 v3-71에서 컨트랙트와 대조해 정정, treasury·fund-fee 즉시 setter는 v3-69, 수수료 요율은 bps로 통일(마이그레이션 0075), 락업은
penalty_type과 분리되고 온체인NO_EARLY⇔lockup_days=0가드는 제거됨(v3-83, v3-66 대체).
읽는 법
- 저장 위치:
DB(Supabase),온체인(컨트랙트 상태),둘 다(DB가 온체인을 미러링). - 분류(08): A는 온체인 setter가 있다(전용 엔드포인트로 수정). B는 온체인인데 setter가 없다(바꾸려면 새 풀을 배포한다). C는 오프체인 전용이다(평범한 PATCH). C-lock은 오프체인이지만 정책으로 잠겨 있다.
- 타임락: 편의상 옮겨 적었다. 정본 값은 08 §timelock에 있다(달라지면 08이 이긴다).
1. Money-path와 지갑
| 필드 | 저장 | 분류 | 변경 주체 | 타임락 | 비고 |
|---|---|---|---|---|---|
fund_wallet | 둘 다 | A | Admin(거버넌스) | 7일 | 파트너 잔여분 목적지이고 파트너가 통제한다. proposeFundWalletChange가 YIELD_DEPOSITOR_ROLE도 함께 옮긴다. |
treasury_wallet | 둘 다 | A* | Admin(거버넌스) | 없음 | Aset 수수료 목적지다. 즉시 setTreasuryWallet(v3-69, 09a)이고 타임락이 없다. 수수료가 자동 분배되므로 지연을 걸어도 낡은 주소에 수수료를 묶어 둘 뿐 잘못된 지급을 막지 못한다. fund_fee_wallet과 같은 구조다. |
fund_fee_wallet | 둘 다 | A* | Admin(거버넌스) | 없음 | FM Pool-mgmt 수수료 목적지다. 즉시 setFundFeeWallet(v3-69)이고 타임락이 없으며 이유는 treasury_wallet과 같다. fund_wallet과 다른 주소여야 한다. |
reserveBalance (reserve_balance mirror) | legacy only | 제거됨 | 새 풀에는 getter 없음 | — | 현재 소스의 외부 리저브 풀은 입금의 reserve_bps 몫을 reserve_wallet으로 보내고 나머지를 fund_wallet으로 보낸다. 풀 내부 리저브 잔고와 getter는 제거됐다. 리저브 비율은 보유 비율이 아니라 외부 지갑으로 보내는 비율이다. 외부 잔액이 풀의 지급 재원으로 자동 보충되지는 않는다. |
상환 지급(request.investor) | 온체인 | — | 불변 | — | 보유가 검증된 요청자다. 지급 함수에 목적지 파라미터가 없다. 운영자가 설정할 수 있는 지급 주소가 존재하지 않는다. |
수익 청구 수령자(msg.sender) | 온체인 | — | 불변 | — | 통제할 수령자 필드 자체가 없다. settleYield의 net 항목은 누구도 지정하지 않고 전역 지분당 누적값만 올린다. 각 투자자의 claimYield는 언제나 자기 호출자(msg.sender)에게 지급한다. 다른 홀더의 수익을 돌리는 것이 구조적으로 불가능하다. |
* treasury_wallet과 fund_fee_wallet은 즉시 어드민 setter(setTreasuryWallet / setFundFeeWallet, 타임락 없음, v3-69)로 적용되는 수수료 경로 거버넌스 변경이다. Class A 필드 표에는 없지만 A처럼 동작한다. 다른 거버넌스 필드와 달리 pool_governance_changes와 7일 propose→execute 플로우를 거치지 않는다.
2. 풀 설정(거버넌스 통제)
| 필드 | 저장 | 분류 | 변경 주체 | 타임락 | 비고 |
|---|---|---|---|---|---|
reserve_bps | DB(온체인 분리를 구동) | A | Admin(거버넌스) | 7일 | 입금의 외부 리저브 지갑 라우팅 비율. 정수 bps(1000 = 10%), 범위 0..10000. 제안·실행에 7일 타임락을 적용하며 증가·감소 모두 가능하다. 게이팅 비율과 다른 필드다. |
capacity(hardCap) | 둘 다 | A | Admin(setHardCap) | 없음(올리는 것만) | 온체인에서 강제한다(v3-71, 옛 §6에서 옮김). setHardCap은 모든 인하를 거부하고 무제한(0)을 유한값으로 좁히는 것도 거부한다. 올리는 것은 투자자를 더 받는 것뿐이라 타임락이 없다(v3-43). 안전망으로 입금 시점에 강제하고 DB capacity가 이를 미러링한다. |
jurisdiction_whitelist와 enforce_jurisdiction | 둘 다 | A | Admin(거버넌스) | 7일 | 지역 제한이다(v3-10/18). enforce_jurisdiction(0064)이 마스터 스위치이고, 화이트리스트는 ISO alpha-3 국가 코드를 담는다(kyc_jurisdiction_whitelist에서 이름 변경, 마이그레이션 0070). 직접 setter는 생성 시 전용이고, 운영 중 변경은 7일 타임락을 거친다. |
nav_per_token | 둘 다 | A | Aset ORACLE(/nav-changes → updateNAV) | 하락 시 24시간 | 상승은 par(1.0 이하)로 제한된다. 편차 상한, 서킷 브레이커, staleness로 경계가 잡힌다(v3-32). ⚠️ 미해결(v3-71): 24시간 하락 지연이 법적 요건인지 운영상 가드인지 확인이 필요하다. |
| KYC 컨트랙트 구현체 | 온체인(UUPS 프록시) | — | Admin(proposeUpgrade) | 즉시 ⚠️ | 업그레이드 가능한 유일한 컨트랙트다(v3-30). money-path는 불변 Clones로 남는다. 2026-08-14 v3-71에서 UPGRADE_TIMELOCK = 0을 채택했다. propose와 execute는 여전히 필요하지만(직접 upgradeToAndCall은 revert한다) 대기 시간이 없다. ⚠️ 보안상 중요한 컨트랙트에 걸린 유일한 업그레이드 타임락이고, 모든 이탈 경로가 이를 읽는다(canRedeem). false를 답하는 구현체는 모든 출금을 막는다. |
풀 단위 KYC/KYB 레벨 게이트는 없다. kyc_level_required와 requires_institutional은 마이그레이션 0063에서 제거됐다. 풀은 더 이상 개인과 법인을 구분해 게이팅하지 않는다. 현재 풀 게이팅은 위의 관할뿐이다. 적격투자자 게이팅은 백엔드 쪽에 구현돼 있다(온체인 변경 없음, 마이그레이션 0072). pools.eligibility_mode(STATUS Status Gate / MIN_TICKET Ticket Gate)와 users.investor_status(RETAIL/PROFESSIONAL)와 qualification_*을 checkKycGating이 강제한다. KYC/KYB·투자자 등급 스펙(v3-74) 참조.
3. 수수료 설정(요율)
| 필드 | 저장 | 분류 | 변경 주체 | 타임락 | 비고 |
|---|---|---|---|---|---|
net_yield_fee_config(JSONB) | DB(JSONB) | C | Admin(PATCH) | 없음 | Lambda가 오프체인에서 계산하고 온체인 쌍이 없다(컨트랙트는 요율을 저장하지 않는다). 수수료 분리는 구현돼 있다(v3-69, 마이그레이션 0067). platform_yield_take_bps(→트레저리) + spc_mgmt_bps(→트레저리) + pool_mgmt_bps(→fund_fee_wallet)이고 3인자 withdrawFees로 오프체인 계산 결과를 적용한다. 모든 요율은 정수 basis point이고(0075에서 *_pct에서 통일. 1% = 100 bps) 0..10000으로 검증한다. perf_fee_bps와 perf_hurdle_bps는 보류·휴면이다(키는 남기지만 어드민 UI가 설정하지 않아 0으로 계산된다). 제거된 게 아니다. |
4. Lifecycle과 운영 플래그
| 필드 | 저장 | 분류 | 변경 주체 | 타임락 | 비고 |
|---|---|---|---|---|---|
is_paused | 둘 다 | A | Admin(/pause → pause()) | 즉시 | 입금만 막고 상환은 정상이다. 온체인이어야 한다(DB만 걸면 우회 가능하다). |
is_emergency_frozen | 둘 다 | A | Pauser Safe(/freeze) | 즉시 | 멈추는 것만 가능하고 자동 만료된다. 72시간 이내에 이탈이 자동 허용되고 동결 전체는 7일 이내에 만료된다. |
lifecycle_status → IMPAIRED | 둘 다 | A | Admin(/lifecycle) | 7일 | 파트너 부실로 입금을 멈추고 출금은 열어 둔다(v3-12). |
lifecycle_status → WIND_DOWN | 둘 다 | A | Admin(proposeWindDown) | 30일 | 종착이다. navPerToken = distributable / totalSupply이고(v3-100) requestRedemption → claim으로만 pro-rata 이탈한다. **30일은 온체인 WIND_DOWN_TIMELOCK**이고, proposeWindDown 전의 "파트너 60일 무응답" 전제조건은 오프체인 정책이다(온체인에서 강제되지 않는다). |
lifecycle_status(그 외 전이) | 둘 다 | A | Admin(/lifecycle) | 즉시 | 예: UPCOMING→ACTIVE→CLOSED→MATURED. |
5. 배포 후 불변 (Class B, 바꾸려면 새 풀)
온체인 상태는 있지만 setter가 없다. DRAFT 동안에만 수정할 수 있고 배포되면 PATCH가 거부된다.
| 필드 | 비고 |
|---|---|
penalty_type / penalty_rate_bps / penalty_fee_amount | 조기 상환 패널티 조건이다. penalty_rate_bps는 정수 basis point이고(예: 5000 = 50%. 1% = 100 bps) 0~1 분수가 아니다(0068). |
lockup_days | 0이면 락업이 없다. v3-83: penalty_type과 독립이다. NO_EARLY 풀도 lockup_days > 0을 가질 수 있다(락업 중에는 LOCKED, 그 뒤에는 무패널티 이탈). 옛 NO_EARLY⇔lockup_days=0 결합과 그 온체인 가드(setLockupDays → NoEarlyRequiresZeroLockup, v3-66)는 제거됐다. 백엔드와 프론트엔드 검증도 더 이상 그 조합을 거부하지 않는다. |
maturity_days | 기간 길이다. 컨트랙트가 갖는 것은 절대 시각 maturityDate이고(v3-145 이후 subscription_end_date + maturity_days, 그 이전에는 deploy_time + maturity_days이며 마이그레이션 0161부터 pools.maturity_date로 미러링된다) 보유분이 아니라 풀의 속성이다. 🔴 FIXED_TERM 풀은 모집 마감일 없이 발행할 수 없다. 기간을 그것부터 재기 때문이다. 📋 만기를 투자자별로 앵커하는 것은 결정됐지만 만들어지지 않았다(v3-131 항목 4). 스토리지에 maturityDays가 없으므로 컨트랙트 변경이 필요하다. 다만 v3-145가 그것을 원했던 이유를 없앴다. 이제 어떤 홀더도 기간보다 짧게 투자돼 있지 않다. |
redemption_type(redemptionType) | ON_DEMAND는 3-state 모델에 따라 이탈을 허용하고, FIXED_MATURITY는 maturityDate까지 이탈을 거부한다. LIQUIDITY_WINDOWS는 삭제됐다(마이그레이션 0106). 온체인에서 epoch_duration_days와 직교한다. 어드민 위저드가 둘을 묶어 놓았었고(v3-88 D7) v3-131 / v3-132가 그 결합을 풀었다. ⚠️ "스키마 변경 없음"은 낡은 말이다. 가드에 대해서는 맞았지만 날짜를 바로잡는 데는 아니었다. 그 뒤로 0189와 0190이 들었다. 컨트랙트 변경은 없다. 함께 추가한 펀딩일 순서 가드는 GovernanceLib이 아니라 엔드포인트에 있다(v3-139). |
min_investment(minInvestment) | 온체인이고 setter가 없다(v3-71, 옛 §6에서 옮김). 입금 시점에 강제한다(BelowMinimumInvestment). 배포 후 불변이고 공시된 투자자 조건이다. |
accepted_currencies | 풀별 스테이블코인이다. 완전히 불변은 아니고 두 setter가 다르게 동작한다. addStablecoin(어드민)은 풀에 LP가 없을 때만(totalSupply() == 0) 동작하고 첫 입금에서 잠긴다(ConfigImmutableAfterDeposit). removeStablecoin에는 그런 입금 게이트가 없다. 대신 가치를 묶어 두지 못하도록 가드가 걸려 있다. LP가 있을 때 풀의 마지막 스테이블코인을 지우는 것을 거부하고, 풀이 잔액을 보유 중인 코인을 지우는 것도 거부한다. 그래서 잔액이 0이고 마지막이 아닌 코인은 입금 이후에도 등록을 해제할 수 있지만, 쓰이고 있는 코인은 안 된다. 앞으로의 정책은 추가만 하는 것이다(v3-71). |
epoch_duration_days(epochDurationDays) | 0이면 즉시, >0이면 회차 pro-rata다. totalLPSupply > 0이 되면 불변이고 상한은 MAX_EPOCH_DURATION_DAYS = 90이다(v3-38). 값 하나가 EARLY 구간과 만기 후 기간을 함께 담당한다. 그것이 무엇을 표현 불가능하게 만드는지는 07 → 구간 × 방식 참조. |
redemption_term_epochs | 🔨 구현됨(0189, dev 적용 대기). 이 이름으로 나갔고 옛 문서는 redemption_window_epochs라 부른다. 만기 후 몇 회차에 걸쳐 상환하는지이고, NULL이면 무제한인데 현재 모든 회차 풀이 그렇다. DB 전용이고 컨트랙트 필드가 없다. 체인은 개수가 아니라 날짜를 갖는다. 생성 시 전용이고 더 이상 선택값이 아니다. 배포의 쓰기 목록, 리마인더, 주기별 엔드포인트가 전부 이를 읽는다(v3-132 · v3-133). 🔴 NULL은 계획이 아님을 뜻하고 아직 기록된 주기가 없음과 절대 뭉치면 안 된다. |
epoch_date_basis · epoch_roll_day | 🔨 구현됨(0189, dev 적용 대기). 사람이 같은 목록을 재현할 수 있도록 남겨 둔 생성 시 규칙이다(CALENDAR / FIXED_DAYS, roll day 1~28). DB 전용이다. 생성 시 전용인데, 실제로 기록된 목록을 계속 설명할 수 있어야 하기 때문이다. 🔴 자체 거버넌스 규칙이 있다(v3-136). 이들이 만들어 낸 날짜는 사람에게 보여 줄 수 있지만 온체인에 쓰거나 스케줄을 결정하게 해서는 안 된다. 읽는 것은 허용되고 실제로 세 화면이 읽는다. |
redemption_epochs.funding_date | 🔨 구현됨(0190, dev 적용 대기). 거버넌스 필드가 아니고 누구도 수정할 수 없다. EpochFundingDateSet의 인덱서 미러이고 관측된 사실만 담는다. 여기 적어 두는 이유는, 체인이 받은 펀딩일과 유도한 펀딩일을 구분해 주는 유일한 것이기 때문이다. 여기에 의도한 날짜를 채워 넣는 쓰기 경로가 생기면 그 구분이 조용히 사라진다(v3-133). 통제 대상인 행위는 POST /pools/{id}/epoch-schedule(ORACLE)을 통한 트랜잭션이다. |
6. 오프체인, 정책으로 잠김 (Class C-lock)
현재 이 분류에 속한 필드가 없다. 예전에 여기 있던(v3-43) min_investment와 capacity는 실제로는 오프체인 정책이 아니라 온체인에서 강제된다. "잠김 / 올리는 것만" 동작이 백엔드 정책이 아니라 컨트랙트 자체에서 나온다(minInvestment에는 setter가 없고 capacity/hardCap은 올리기 전용 setHardCap을 쓴다). 각각 §5와 §2로 옮겼다(v3-71). 앞으로 오프체인 전용 정책 잠금이 생길 경우를 위해 분류 정의만 남겨 둔다.
관련 문서
- 08-smart-contracts: 온체인 값 동기화(Class A/B/C) · 타임락 정본
- 08a-contract-reference: 역할별 접근 통제
- 09a-custody: money-path 불변성과 비수탁
- 11-db-schema: 전체 컬럼 목록
- 23-money-path: 돈이 어디로 움직이는지(자본과 수수료 흐름)
- 14-decisions: v3-71(매트릭스 정정), v3-70(네이밍), v3-69(수수료 분리), v3-43(C-lock), v3-38(회차 불변성)