Skip to content

필드 거버넌스 매트릭스

한눈에 보는 내부 참조다. 거버넌스와 관련된 펀드·풀 필드마다 어디에 저장되는지(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_EARLYlockup_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둘 다AAdmin(거버넌스)7일파트너 잔여분 목적지이고 파트너가 통제한다. proposeFundWalletChangeYIELD_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_walletfund_fee_wallet즉시 어드민 setter(setTreasuryWallet / setFundFeeWallet, 타임락 없음, v3-69)로 적용되는 수수료 경로 거버넌스 변경이다. Class A 필드 표에는 없지만 A처럼 동작한다. 다른 거버넌스 필드와 달리 pool_governance_changes와 7일 propose→execute 플로우를 거치지 않는다.

2. 풀 설정(거버넌스 통제)

필드저장분류변경 주체타임락비고
reserve_bpsDB(온체인 분리를 구동)AAdmin(거버넌스)7일입금의 외부 리저브 지갑 라우팅 비율. 정수 bps(1000 = 10%), 범위 0..10000. 제안·실행에 7일 타임락을 적용하며 증가·감소 모두 가능하다. 게이팅 비율과 다른 필드다.
capacity(hardCap)둘 다AAdmin(setHardCap)없음(올리는 것만)온체인에서 강제한다(v3-71, 옛 §6에서 옮김). setHardCap모든 인하를 거부하고 무제한(0)을 유한값으로 좁히는 것도 거부한다. 올리는 것은 투자자를 더 받는 것뿐이라 타임락이 없다(v3-43). 안전망으로 입금 시점에 강제하고 DB capacity가 이를 미러링한다.
jurisdiction_whitelistenforce_jurisdiction둘 다AAdmin(거버넌스)7일지역 제한이다(v3-10/18). enforce_jurisdiction(0064)이 마스터 스위치이고, 화이트리스트는 ISO alpha-3 국가 코드를 담는다(kyc_jurisdiction_whitelist에서 이름 변경, 마이그레이션 0070). 직접 setter는 생성 시 전용이고, 운영 중 변경은 7일 타임락을 거친다.
nav_per_token둘 다AAset ORACLE(/nav-changesupdateNAV)하락 시 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_requiredrequires_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)CAdmin(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_bpsperf_hurdle_bps보류·휴면이다(키는 남기지만 어드민 UI가 설정하지 않아 0으로 계산된다). 제거된 게 아니다.

4. Lifecycle과 운영 플래그

필드저장분류변경 주체타임락비고
is_paused둘 다AAdmin(/pausepause())즉시입금만 막고 상환은 정상이다. 온체인이어야 한다(DB만 걸면 우회 가능하다).
is_emergency_frozen둘 다APauser Safe(/freeze)즉시멈추는 것만 가능하고 자동 만료된다. 72시간 이내에 이탈이 자동 허용되고 동결 전체는 7일 이내에 만료된다.
lifecycle_statusIMPAIRED둘 다AAdmin(/lifecycle)7일파트너 부실로 입금을 멈추고 출금은 열어 둔다(v3-12).
lifecycle_statusWIND_DOWN둘 다AAdmin(proposeWindDown)30일종착이다. navPerToken = distributable / totalSupply이고(v3-100) requestRedemption → claim으로만 pro-rata 이탈한다. **30일은 온체인 WIND_DOWN_TIMELOCK**이고, proposeWindDown 전의 "파트너 60일 무응답" 전제조건은 오프체인 정책이다(온체인에서 강제되지 않는다).
lifecycle_status(그 외 전이)둘 다AAdmin(/lifecycle)즉시예: UPCOMINGACTIVECLOSEDMATURED.

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_days0이면 락업이 없다. v3-83: penalty_type과 독립이다. NO_EARLY 풀도 lockup_days > 0을 가질 수 있다(락업 중에는 LOCKED, 그 뒤에는 무패널티 이탈). 옛 NO_EARLYlockup_days=0 결합과 그 온체인 가드(setLockupDaysNoEarlyRequiresZeroLockup, 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_MATURITYmaturityDate까지 이탈을 거부한다. 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_investmentcapacity는 실제로는 오프체인 정책이 아니라 온체인에서 강제된다. "잠김 / 올리는 것만" 동작이 백엔드 정책이 아니라 컨트랙트 자체에서 나온다(minInvestment에는 setter가 없고 capacity/hardCap은 올리기 전용 setHardCap을 쓴다). 각각 §5§2로 옮겼다(v3-71). 앞으로 오프체인 전용 정책 잠금이 생길 경우를 위해 분류 정의만 남겨 둔다.