Joob 풀 설정(계약 기준)
작업 진행 중
v3.0 차원 모델을 적용했다. 아래 매핑이 갱신본이다. pool_type, lp_issuance_model, collateral_type enum을 쓰던 v2.x 페이로드는 폐기됐다.
SSA 계약(EV3: FJOS/ShardLab, EV4: Henon/Hashed)과 Investment Memo에서 뽑은 풀 생성 파라미터를 v3.0 차원에 매핑했다.
🚧 DPD에서 OJK NAV 상각으로 이어지는 자동화는 보류됐다 (2026-07-01)
대출별 DPD를 받아 OJK 상각 스케줄을 NAV에 적용하려던 v3-13 자동 파이프라인은 만들어지지 않았다. Joob의 대출별 데이터에 막혀 있다(guest API는 집계 DPD 구간만 노출한다. 펀드 라우트는 30/60/90, 마스터 라우트는 30/60과 /risk/nonperforming이고, 대출별 원금이 없다). 현재 report.scheduler.fetch는 DPD·RNI·NPL을 표시용 관측값으로 기록만 하고, loan_writeoffs에는 라이터가 없으며, NAV 상각은 POST /nav-changes로 수동으로 한다. Writedown & NAV → DPD 자동 상각 참조.
이와 별개로 표시·반입 구간 모델을 고정 30/60/90에서 관할이 정의하는 동적 구간(pool_dpd_buckets 자식 테이블)으로 일반화하는 중이다. 그래야 Joob이 아닌 규제 체계(예: OJK 90/120/180)를 받을 수 있다. v3-59 참조(🚧 구현 예정).
Joob 풀을 만들 때 이 문서를 볼 것
아래 값들은 v3.0 차원 모델에서 pools 테이블 컬럼에 그대로 대응된다. 시나리오 둘을 문서화했다.
- Joob 신규 투자자 풀: 신규 투자자가 USDC를 예치하고 Aset이 LP를 민팅하며 파트너 잔여분이 Joob의
fund_wallet으로 간다. - Joob 레거시 보유분(미러 커스터디): 기존 ShardLab/Hashed의 RPS 포지션을 플랫폼에 보여 주되 투자는 할 수 없다.
custody_mode = 'MIRROR'(0199)다. ⚠️ 이 시나리오는 0203 전까지is_display_only = true라고 적혀 있었다. 그 컬럼은 이제is_showcase이고 v3-40 마케팅 등급만 담는데, 그러면 이 풀이 보여 주려고 존재하는 Fund Data 탭이 숨겨진다.
Guest API 표면: 펀드와 마스터 (문서 "Master Only", 2026-08-19 반영)
guest API는 같은 네 개념을 두 라우트 계열로 제공하고, 풀은 pools.external_entity_type(FUND 기본 | MASTER, 마이그레이션 0194)으로 하나를 고른다. 현재 모든 매핑이 FUND이고 저절로 바뀌는 것은 없다.
FUND(/api/guest/fund/{id}) | MASTER(/api/guest/master/{id}) | |
|---|---|---|
| 발견 | whoami.fundIdsAuthorized | whoami.masterIdsAuthorized. 마스터는 당신의 펀드를 활성 서브펀드로 보유하고 있을 때만 나타난다 |
/summary | fund + snapshot + fxRate | master(compositionVersion, subfundCount), position(이 LP의 ownershipShare, grossReturn, converted), fxRate.mixed/lpCurrency/fundCurrency가 더해진다 |
/history 행 | source(chain|ledger) | trigger(daily|attach|detach|manual|backfill)와 compositionVersion. UTC 일자당 한 행으로 병합된다 |
/risk/dpd/{b} | 30 · 60 · 90 | 30 · 60만. 90은 400을 답한다(dpd-exclude-90) |
| 90일 이상 모집단 | 구간 90 | /risk/nonperforming → pendingWriteOff(장부에 있음) + writtenOff(상각됨) |
마스터 라우트가 보고하는 것은 이 풀의 펀드가 아니다. 실제로 마스터 2는 서브펀드 둘을 연결하므로 DPD-30 노출이 719,308,325 EFIDR인데 펀드 1은 1,075,925다. master.totalSubscribed는 17,525,000,000이고 펀드는 8,950,000,000이다. position.ownershipShare(실측 0.2386)는 마스터 AUM에 대한 LP의 몫이고 백분율이 아니라 분수다. 그러므로 풀을 MASTER로 바꾸면 보고 가치가 덮는 범위가 달라지고, 이 풀이 직접 권한을 갖지 않은 서브펀드까지 포함된다. 마이그레이션 기본값이 아니라 의도적인 결정이어야 한다.
실제로 물리는 계약 사항:
- 모든 응답이 HTTP 200이다. 성공이든 실패든 그렇고, 결과는 본문의
code에 있다. 전송 상태로 분기하면 401/403/400/503을 데이터 없는 성공으로 읽게 된다. snapshot,startDate,endDate,snapshotDate, 그리고 마스터의fundValue/totalRni는 nullable이다. null은 "기록되지 않음"이지 0이 아니다. 실제 마스터 history는 225일 중 20일이 null 값이다./history는 호출당 366일로 제한되고, 해당 엔티티의 시작일보다 이른from을 거부한다.to는 미래일 수 없다(UTC). 셋 다 클램프하지 않고 400을 답한다.- 마스터의 DPD와 nonperforming은 일이 아니라 월(
YYYY-MM) 단위로 키잉된다. - 금액은 이미 사람이 읽는 단위로 스케일된 소수 문자열이고 wei가 아니다. 1e18로 더 나눌 필요가 없다.
- webhook이 없고(폴링만) 클라이언트 캐싱도 없으며, 라우트별
503킬 스위치가 있는데(masterSummaryEnabled/masterHistoryEnabled/masterDpdEnabled)Retry-After가 없다. 그래서 지수 백오프를 쓴다.
시나리오 1: Joob 신규 투자자 풀 (v3.0 활성 풀)
| 차원 | 값 | 출처 | 비고 |
|---|---|---|---|
name | "Joob IDN Private Credit 1" | Investment Memo | |
chain_id | 1(Ethereum 메인넷) | Joob/Henon | OneSafe를 통한 메인넷 USDC |
fund_wallet | Joob의 OneSafe 주소 | Joob 운영 | 입금의 파트너 잔여분이 여기로 간다 |
is_showcase | false | — | 투자 가능한 풀 |
partner_id | Joob UUID | 플랫폼 설정 | |
| NAV 처리 | Aset 오라클 Lambda | v3-15 | NAV는 항상 Aset이 판단한다(nav_data_source 차원 없음). DPD→OJK 자동 상각(v3-13)은 보류돼 있고 만들어지지 않았다. 아래 주의 참조. DPD는 표시용으로만 캐시되고 NAV는 POST /nav-changes로 수동 설정한다. |
| 트랜치 | 단독(tranche_group_id 없음) | v3-14 | 단일 풀이고 Senior/Junior가 없다. 트랜치 상품은 tranche_group_id를 공유하는 별도 풀들을 쓴다. |
maturity_model | FIXED_TERM | SSA Schedule 2 | 12개월 기간 |
lockup_days | 90 | SSA Schedule 2 §4.2 | 3개월 락업 |
maturity_days | 365 | SSA Schedule 2 §4.1 | 12개월 |
target_size | 500000 | SSA EV3 + EV4 확인 | 25만 달러(ShardLab) + 25만 달러(Hashed) = 50만 달러 |
target_size_max | null | — | 아직 확정 상한 없음 |
min_investment | 100000 | Investment Memo | 10만 달러 |
apy_rate | 15 | SSA §2.3(13~17% 범위) | 중간값 |
apy_disclosure | "Target 13–17% APY; actual varies with asset utilization, product mix, and NPL" | Investment Memo | |
reserve_bps | 1000 | v3.0 기본값 | Pool 컨트랙트에 보유하는 상환 유동성 reserve(1000 bps = 10%)다. first-loss 버퍼가 아니다(R8). 이 단독 풀의 first-loss는 운용사 equity 버퍼(R6)이고 그 요율은 아직 계약 확인 대기 중이다 |
penalty_type | YIELD_BASED | SSA §4.2 | 배당 50% 몰수 |
penalty_rate_bps | 5000 | SSA §4.2 | 발생 수익의 50%(5000 bps) |
yield_frequency | "QUARTERLY" | SSA §1.2 | |
yield_trigger | (v3-20에서 삭제) | SSA §1.1–1.2 | 컬럼이 제거됐다. 수익은 기본이 수동 청구이고, FJO가 선언하면 Aset이 그다음에 depositYield를 실행한다 |
collateral_description | "100% collateralized by Indonesian loan receivables (BCA Wholesale Loans, EWA, etc.)" | Investment Memo | v3.0에서는 자유 텍스트 |
collateral_ratio | 100 | Investment Memo | 100% 담보 |
kyc_level_required | KYC | 플랫폼 기본값 | 개인 리테일 가능 |
jurisdiction_whitelist | [] | — | 모든 관할 허용 |
accepted_currencies | {USDC} | Joob | Ethereum 메인넷의 USDC |
redemption_gating_bps | null | — | 회차별 상한 없음 |
is_paused | false | — | |
is_emergency_frozen | false | — | |
npl_threshold_days | 90 | 2026-07-24 Joob 통화 | 90 DPD 이상 대출은 부실이지만 장부에 남는다(npl_ratio에 들어간다). 상각은 별개이고 나중이다(아래) |
write_off_policy | "약 6개월(≈180 DPD)에 상각" | 2026-07-24 Joob 통화 | 대출이 장부에서 제거돼 실현 손실이 되고 cumulative_loss에 들어간다(Joob의 "상각됨 / 회수 중" 항목). 90 DPD의 NPL 표시와는 별개다. 펀드 가치 기준 참조 |
lifecycle_status | DRAFT → UPCOMING → ACTIVE | 스케줄러 | 배포 이후 |
수익 계산
Joob의 total_rni는 수수료를 떼기 전 총액이다. Aset Lambda가 Pool.settleYield()를 위해 net을 계산해야 한다.
net_yield = total_rni
− admin_fee (연 1%, 분기 지급)
− performance_fee (15% hurdle 초과 수익의 20%, 만기에)성과 수수료는 만기에만 발생한다. 분기 분배에는 gross_rni − admin_fee만 쓴다.
펀드 가치 기준과 손실 인식: 2026-07-24 Joob 통화
current_fund_value는 현금 수취 기준이고 Aset은 그대로 미러링한다. Joob이 보고하는 펀드 가치는 실제로 수취한 이자만 세고 승인됐지만 미수취인(미수) 이자는 세지 않는다. 승인에서 수취까지 시차가 1~2일 정도라(15% APY에서 하루치 이자, 약 0.04%로 NAV 표시 정밀도 아래다) Aset은 NAV를원금 + 현금 + 미수 − 손실로 재구성하는 대신 현금 기준fund_value를 그대로 받는다.accrued_income요청은 철회했고 그 스냅샷 컬럼(마이그레이션 0068)은 휴면 상태로 둔다. 분기 단위 등 유의미한 발생 불일치가 나타나면 다시 검토한다. Joob은 펀드 가치도 DPD처럼 매일 09:00 EOD 갱신으로 옮기고 있어서 반입이 하루 한 번 이뤄진다.- 90에 NPL, 약 180 DPD에 상각이고 둘은 별개다. Joob은 대출을 90 DPD에 부실로 분류하고(장부에 남아 DPD-90 구간에 있으며
npl_ratio에 들어간다) 약 6개월(≈180 DPD)에 상각해 실현cumulative_loss로 넘긴다(Joob의 별도 "상각됨 / 회수 중" 항목). 그래서npl_threshold_days = 90과write_off_policy ≈ 180이 합쳐지지 않는다. 실제로 90~180 DPD의 장부상 NPL 구간이 있고, 리스크 배지의 선행 NPL 신호가 정상적으로 작동한다.
패널티 유형 정리
SSA §4.2의 패널티 산식은 B = 상환일까지 지급된 배당의 50%다. v3.0 용어로는 penalty_type = YIELD_BASED, penalty_rate_bps = 5000(50%)이다.
상환 시점에 배당이 지급된 적이 없으면, SSA 단서에 따라 NPL 손실(청약금의 최대 5%)이 상환 가격에 더해진다. Pool.approveRedemption()을 호출하기 전에 Aset Lambda에서 오프체인으로 처리한다.
시나리오 2: Joob 레거시 보유분(표시 전용)
ShardLab과 Hashed의 기존 RPS 포지션이 Kaia에 있다(0x3f9c024E948Ae2A6554331563D5197f7E6441403). 투자자가 볼 수 있도록 플랫폼에 표시하되 Aset을 통한 입금이나 상환은 되지 않는다.
| 차원 | 값 | 비고 |
|---|---|---|
custody_mode | 'MIRROR' | 우리를 통한 자본 유출입 경로가 없다. 읽기 모델은 다른 풀과 동일하다 |
is_showcase | false | v3-40 마케팅 등급은 Performance와 Fund Data를 숨긴다. 이 풀에는 맞지 않는다 |
chain_id | 8217(Kaia) | 기존 RPS 토큰 |
lp_token_address | 0x3f9c024E948Ae2A6554331563D5197f7E6441403 | 외부 RPS 컨트랙트 |
fund_wallet | 해당 없음 | 입금이 없다 |
| NAV 출처 | Joob API(읽기 전용 미러) | 제안이 아니라 기록이다. derive 단계가 attested_off_chain = true로 nav_history를 쓰고(0201) updateNAV를 보내지 않는다 |
| 모든 플로우 차원 | 해당 없음 | Aset을 통한 입금·상환이 없다 |
표시 전용 풀은 잔액, NAV, 발생 수익을 보여 준다. 입금이나 상환 CTA는 없다.
풀 생성 페이로드 (v3.0)
{
"name": "Joob IDN Private Credit 1",
"chain_id": 1,
"fund_wallet": "0x...joob_onesafe_address...",
"is_showcase": false,
"partner_id": "<joob-uuid>",
"maturity_model": "FIXED_TERM",
"lockup_days": 90,
"maturity_days": 365,
"target_size": 500000,
"min_investment": 100000,
"apy_rate": 15,
"apy_disclosure": "Target 13–17% APY; actual varies with asset utilization, product mix, and NPL",
"reserve_bps": 1000,
"penalty_type": "YIELD_BASED",
"penalty_rate_bps": 5000,
"yield_frequency": "QUARTERLY",
"collateral_description": "100% collateralized by Indonesian loan receivables",
"collateral_ratio": 100,
"kyc_level_required": "KYC",
"jurisdiction_whitelist": [],
"accepted_currencies": ["USDC"],
"lifecycle_status": "DRAFT"
}미해결 항목
target_size는 50만 달러다(ShardLab 25만 + Hashed 25만 확인). 현재 어드민 패널의 capacity($534,997.01)는 이전 추정치의 EFIDR/USD 환산이 반영된 값이므로, 다음 동기화 때 50만으로 맞춰야 한다.- 어드민의 활성 TVL($573K)에는 PM과 백엔드 개발자의 1만 달러 테스트 입금 두 건이 포함돼 있다(실제 투자자 자본이 아니다). 실제 투자자 자본은 50만 달러다.
start_date와end_date는 실제 SSA 완료일에 따라 정한다.fund_wallet: 배포 전에 Joob의 OneSafe Ethereum 메인넷 주소를 확인해야 한다.partner_id: 파트너 엔티티를 먼저 생성해야 한다.
v2.x → v3.0 매핑
| v2.x | v3.0 |
|---|---|
pool_type: FUND_POOL | (fund_wallet과 partner_id에서 파생) |
lp_issuance_model: FUND_ISSUED | 제거됨. LP는 항상 Aset이 민팅 |
lp_token_address: 0x3f9c...Kaia | custody_mode = 'MIRROR' 레거시 풀에만 해당 |
collateral_type: FULLY_COLLATERALIZED | collateral_description + collateral_ratio: 100 |
penalty_type: CUSTOM | penalty_type: YIELD_BASED, penalty_rate_bps: 5000 |
yield_trigger: MANUAL_CLAIM | v3-20에서 필드 삭제. 수동 청구가 모든 v3.0 풀의 기본 동작이다 |
operating_currency: EFIDR / fx_rate: 16730 | 오프체인 Lambda의 관심사이고 pools 테이블에 저장하지 않는다 |