Skip to content
Open
82 changes: 82 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,85 @@ passing
요구하지 않음), production legal 활성화, look-through graph 실제 순회.
- Deferred follow-up: 6개 element를 실제 recipe(`requiredElements`)에 연결할지,
연결한다면 어떤 recipe에 붙일지는 별도 feature에서 결정한다.

## CMP-004 — Wave-2b In-Place Element Upgrades (A-01/A-03/A-04/A-13/B-01/B-02) + CLI Decode

### Behavior

- 기존 6개 illustrative mock element — A-01 Sanctions, A-03 AccreditedInvestor,
A-04 IdentityUniqueness, A-13 QualifiedPurchaser, B-01 AssetClassification,
B-02 Erc3643Native — 를 동일 contract 파일·registry ID·recipe 배선을 유지한
채 각 element walkthrough 문서의 전체 실패코드 taxonomy로 in-place
업그레이드했다: A-01 10개, A-03/A-04/A-13 각 9개, B-01/B-02 각 6개
reasonCode(`ReasonCodes.encode(0, ELEMENT_ID, n)`, n -> doc §6.x 이름 —
각 contract 헤더 주석 테이블 참고).
- 호환성 규칙(non-negotiable): 기존 operator setter —
`Sanctions.setBlocked`, `AccreditedInvestor.setAccredited`,
`IdentityUniqueness.bindIdentity`, `QualifiedPurchaser.setQp`,
`AssetClassification.setClassification`, `Erc3643Native.setErc3643Native`
— 는 시그니처와 legacy happy-path 효과(그 호출만으로 `check` PASS)를
그대로 유지한다: 각 setter가 내부적으로 완전히 유효한
claim/card(trusted issuer, valid signature, no expiry)를 대신 기록한다.
- 새로 추가된 strictness(claim pipeline, look-through 게이트, 시간/신선도
경계, B-02 live-wiring 등)는 전부 operator-gated opt-in이며 기본값
off(`false`/`0`)다 — 이 config를 건드리지 않는 기존 흐름은 legacy 동작
그대로 유지된다.
- `services/cli/src/reason.ts`의 reason-code decode table을 확장했다:
- 업그레이드된 6개 element의 신규 code range를 각 contract 헤더의
doc-name으로 채운 `ELEMENT_CODE_NAMES` 테이블을 추가(예: A-01 code 4 ->
`FAIL_NO_SANCTIONS_CLAIM`).
- `ELEMENT_LABELS`에서 누락돼 있던 wave-2(CMP-003) 6개 element(A-08 1-8,
A-09 1-2, A-11 1-5, B-03 1-6, B-04 1-7, D-01 1-4)도 등록해
`DeployStack`이 실제로 등록하는 17개 element 전체와 맞췄다(이전 주석은
"11개 element"라고 잘못 명시하고 있었음).
- 신규 code가 없는 5개 element(A-02/A-05/C-01/E-01/F-02)는 기존처럼
code 1 fallback을 유지한다.
- **recipeId 축 추가**: `ComplianceEngine._runChecks`는 실패 시 항상
`encode(contributingRecipe[i], elementId, 1)`로 code를 1로 재-encode
하므로(엔진 코드 미변경 범위), recipe-scoped(`{1,2,7}`) 조합에서 실제로
도달 가능한 code는 여전히 1뿐이다. 반면 각 element의 `check()`는 항상
`ReasonCodes.encode(0, ELEMENT_ID, n)`으로 스스로를 self-encode하며(실제
상세 코드가 나타나는 지점), D-01 HolderCount의 `onTransfer`는 이 값으로
직접 `Errors.ComplianceRejected`를 revert한다. 따라서 recipeId 0 축을
별도로 추가해(엔진과 무관하게 element 직접 호출·revert·이벤트에서 실제로
관측되는 값) 신규 code들이 실질적으로 디코딩되도록 했다(기존 recipeId
`{1,2,7}` 축도 audit-matching 용도로 유지, code 범위만 확장).

### Verification

- `forge build --offline`.
- `forge test --offline`: 39 suites, 579 passed, 0 failed, 0 skipped(레거시
`test/unit/compliance/Elements.t.sol`의 Sanctions/AccreditedInvestor/
QualifiedPurchaser 케이스 불변 + 각 element 전용
`test/unit/compliance/elements/{Sanctions,AccreditedInvestor,
IdentityUniqueness,QualifiedPurchaser,AssetClassification,
Erc3643Native}.t.sol`에 신규 code·경계 테스트 다수 추가 — 개별 U1-U6
커밋에서 이미 작성·통과).
- `cd services/cli && npm test`: reason-table 크기 회귀를
`4 * 86(elements 코드 합) + 6(policy)` = 350으로 갱신하고, `cast keccak`로
독립 검증한 신규 ground-truth(A-01 direct code 4, D-01 direct code 3)
decode 회귀 및 A-01 code 1 legacy 의미 보존 회귀를 추가.
- `cd services/rfq && npm test`.
- 수동 확인(코드 변경 없음): `script/DeployStack.s.sol`의 KYC/investor-setup
헬퍼(`setClassification`, `setErc3643Native`, `bindIdentity`,
`setAccredited`)와 `test/integration/IntegrationBase.sol`의 9-element
attestation 헬퍼(`setAccredited`/`setQp`/`setBlocked`/`bindIdentity`)가
legacy 시그니처 그대로 호출됨을 확인; `scripts/e2e-anvil.sh`는 CLI를
전혀 호출하지 않고 `DeployStack`/`DemoScenarios` forge script만
구동하므로 이번 CLI 변경과 무관함을 확인.

### State

passing

### Notes

- Non-goals: 이번 entry는 U1-U6에서 이미 완료된 6개 element 자체의 스펙
변경(claim pipeline, look-through 등, 커밋 `a99f4ae`..`5a36111`)을 다시
구현하지 않는다 — CLI decode table 확장과 통합 문서화·전체 검증에
한정된다.
- `ComplianceEngine`의 코드-1 하드코딩(재-encode) 자체는 엔진 파일 편집 금지
범위 밖이라 이번 wave에서 고치지 않았다 — richer per-element propagation은
별도 feature로 이연.
- Deferred follow-up: CMP-003과 동일하게 wave-2 6개 element의 recipe 연결
여부는 미결.
69 changes: 49 additions & 20 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ source of truth로 사용한다.
A-04/A-03 주소 + `CapMode.TWELVE_G`로 생성 후 engine 생성 이후 `setEngine`).
어떤 recipe의 `requiredElements`에도 연결하지 않음(별도 feature로 이연).
`forge test --offline` 399/399 유지.
- `CMP-004 — Wave-2b In-Place Element Upgrades + CLI Decode`(A-01 Sanctions,
A-03 AccreditedInvestor, A-04 IdentityUniqueness, A-13 QualifiedPurchaser,
B-01 AssetClassification, B-02 Erc3643Native를 동일 ID·배선을 유지한 채
walkthrough-doc 실패코드 taxonomy로 in-place 업그레이드, legacy setter
호환·신규 strictness는 opt-in default-off; `services/cli/src/reason.ts`
decode table을 신규 code range + 누락됐던 wave-2 6개 element로 확장하고
recipeId 0(element 직접 self-encode) 축을 추가):
`forge test --offline` 579/579, `services/cli`/`services/rfq` npm test
통과.

## Blocked

Expand All @@ -86,32 +95,52 @@ source of truth로 사용한다.
6. Order Book은 matching/custody/surveillance 모델 결정 후 구현한다.
7. CMP-003의 6개 wave-2 element(A-08/A-09/A-11/B-03/B-04/D-01)를 실제 recipe
`requiredElements`에 연결할지, 연결한다면 어떤 recipe에 붙일지 결정한다.
8. `ComplianceEngine._runChecks`가 실패한 element의 실제 reasonCode 대신 항상
code 1로 재-encode하는 한계(CMP-004에서 확인)를 해소할지 결정한다 — richer
per-element propagation을 엔진에 배선하면 CLI/off-chain audit가 recipe-scoped
경로에서도 신규 실패코드(A-01 code 2-10 등)를 직접 관측할 수 있다.

## Last Session Summary

- `CMP-003 — Wave-2 Illustrative Elements`: A-08/A-09/A-11/B-03/B-04/D-01
6개 mock element(사전 세션에서 개별 구현 + unit test 완료)를
`script/DeployStack.s.sol`에 등록하는 integration 작업.
- `CMP-004 — Wave-2b In-Place Element Upgrades + CLI Decode` (U7 integration
task, `feat/element-upgrades` branch): 6개 element(A-01/A-03/A-04/A-13/B-01/
B-02) 자체의 walkthrough-doc 업그레이드는 이전 세션들(U1-U6, 커밋
`a99f4ae`..`5a36111`)에서 이미 완료돼 있었고, 이번 세션은 그 위에 CLI decode
table 확장 + 통합 문서화 + 전체 회귀 검증을 수행했다.
- 변경한 파일:
- `script/DeployStack.s.sol`
- `services/cli/src/reason.ts`
- `services/cli/test/smoke.ts`
- `FEATURES.md`, `PROGRESS.md`
- 완료한 작업:
- A-09(EquityOwnerLookThrough)를 먼저 배포하고 그 주소를 A-08
(EntityEligibility) 생성자에 `ILookThroughSource`로 주입
- A-11(ClaimFreshness), B-03(TransferRestrictionMetadata),
B-04(EngineSelection)를 문서화된 생성자 인자로 배포·등록
- D-01(HolderCount)을 위해 A-04(IdentityUniqueness)/A-03(AccreditedInvestor)
생성 결과를 state 변수로 캡처하도록 최소 리팩터링하고, `CapMode.TWELVE_G` +
두 주소로 생성 후 engine 생성 이후 `setEngine`으로 post-trade write path
연결(F-02 SurveillanceFlag와 동일 패턴)
- recipe `requiredElements`는 의도적으로 변경하지 않음(별도 feature로 이연)
- `reason.ts`에 6개 업그레이드 element의 신규 code range를 각 contract
헤더 doc-name으로 채운 `ELEMENT_CODE_NAMES` 테이블 추가.
- `ELEMENT_LABELS`에서 누락돼 있던 wave-2(CMP-003) 6개 element(A-08/A-09/
A-11/B-03/B-04/D-01)를 등록해 `DeployStack`이 실제 등록하는 17개 element
전체와 일치시킴(이전 주석의 "11개 element"는 stale이었음).
- 코드 추적 결과 `ComplianceEngine._runChecks`가 실패 시 항상 code 1로
재-encode함을 확인 — recipe-scoped(`{1,2,7}`) 조합에서 code 2+ 는 오늘
실제로 도달 불가능하다. 반면 모든 element의 `check()`는 스스로
`ReasonCodes.encode(0, ELEMENT_ID, n)`으로 self-encode하고(D-01
HolderCount는 이 값으로 직접 revert), 이것이 신규 code들이 실제로 관측되는
경로다. 그래서 recipeId 0 축(element 직접 코드)을 테이블에 별도로
추가했다 — 기존 recipe-scoped 축(code 1만, audit-matching 용도)은 그대로 유지.
- `smoke.ts`의 reason-table 크기 회귀를 `4 * 86 + 6 = 350`으로 갱신하고,
`cast keccak`로 독립 검증한 신규 ground-truth(A-01 direct code 4, D-01
direct code 3) decode 회귀와 A-01 code 1 legacy 의미 보존 회귀를 추가.
- `script/DeployStack.s.sol`(KYC/investor-setup 헬퍼)과
`test/integration/IntegrationBase.sol`(9-element attestation 헬퍼)이
legacy setter 시그니처 그대로 호출됨을 확인 — 코드 변경 없음.
`scripts/e2e-anvil.sh`는 CLI를 전혀 호출하지 않으므로(순수
`DeployStack`/`DemoScenarios` forge script 구동) 이번 변경과 무관함을 확인.
- 실행한 검증:
- `forge build --offline`
- `forge test --offline`(전체 399/399 유지; 기존 test 조정 불필요 —
`DeployStack.s.sol`은 forge test 경로에서 실행되지 않음)
- `forge fmt script/DeployStack.s.sol`
- `forge test --offline`: 39 suites, 579 passed, 0 failed, 0 skipped
- `cd services/cli && npm test`
- `cd services/rfq && npm test`
- `forge fmt` 대상 없음(.sol 변경 없음)
- 남은 리스크:
- 6개 element는 아직 어떤 recipe에도 연결되지 않아 trade-path 동작이
검증되지 않았다(단위 테스트만 존재).
- `DeployStack.s.sol`의 실제 라이브 Anvil 배포/CLI 경로 재실행(`scripts/e2e-anvil.sh`)은
이번 세션에서 수행하지 않았다.
- `ComplianceEngine`의 code-1 하드코딩(위 참고)은 이번 세션에서 고치지
않았다 — 엔진 파일 편집은 이 wave의 범위 밖. richer per-element
propagation을 원한다면 별도 feature로 분리해야 한다(Next #8).
- CMP-003의 wave-2 6개 element는 여전히 어떤 recipe에도 연결돼 있지 않다
(Next #7, 변경 없음).
Loading
Loading