Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -901,3 +901,83 @@ review and post-deployment reconciliation unreliable.
- `services/cli/src/commands.ts`
- `docs/architecture/asset-manifest.md`
- `docs/architecture/compliance-policy.md`

## D018 — Asset-specific Element values belong to the Manifest compiled plan

Date: 2026-09-08

### Context

The BUIDL-like reference profile encoded `5,000,000 ether` in three places and
bound a BUIDL-named Element/Recipe even though qualified-purchaser status and a
commercial minimum are independent predicates. Phase 3 review material and the
open ADR-010 discussion in PRs #90 and #97 identified the same ownership issue:
immutable Element code should define reusable logic while the token Manifest
should own asset-specific values.

The commercial meaning of the demo threshold remains unresolved. Available
inputs do not establish whether a real product would apply it to subscription,
buy-only secondary trades, every trade, or post-trade holdings.

### Decision

1. A Manifest may bind a bounded non-empty byte value to an immutable
`elementId`. The registry rejects duplicate, unused, oversized or zero-length
parameter entries and limits both entry count and value length to 256.
2. The registry aligns parameter bytes with compiled Element rules and commits
them into each parameterized binding hash and the aggregate plan hash.
Semantic changes therefore use the existing Manifest timelock.
3. A parameterized rule receives `abi.encode(ComplianceContext, bytes)` through
the existing `IComplianceElement.check(..., bytes context)` ABI. A rule with
no parameter receives the exact legacy `abi.encode(ComplianceContext)` value,
and a fully parameterless binding keeps its legacy hash construction.
4. `MIN-TRADE-v1` defines only the reusable inclusive per-trade regulated-asset
amount predicate. Its parameter schema is `abi.encode(uint256 minimumAmount)`;
missing, malformed or zero values fail closed.
5. The BUIDL-like demo composes Reg D 506(c), 3(c)(7) QP and minimum-trade as
three independent bindings. It preserves the existing `5,000,000 ether`
behavior in both trade directions only as a reference-demo behavior lock.
6. Legacy BUIDL-specific contracts remain for source and deployed-version
compatibility but are not registered by new demo/testnet deployments.

### Alternatives Considered

- Constructor-configured Element instances per asset: rejected because it
duplicates bytecode and registry entries instead of preserving reusable
immutable predicates.
- Put values in Recipe bytecode: rejected because it recreates an asset-specific
Recipe and makes value changes require a new implementation/version.
- Change the stable Element interface to add an explicit `elementId` and
parameter argument: deferred because the existing context byte boundary can
carry the compiled value without a repository-wide ABI migration. A future
interface version may make this shape explicit.
- Infer that the BUIDL-like threshold is a real BUIDL subscription rule:
rejected because the repository has no approved product/legal evidence for
that claim.

### Consequences

- One generic predicate can be reused across assets with independently reviewed
Manifest values and deterministic Safe/onboarding commitments.
- Dynamic `bytes` storage and context encoding add registration and evaluation
gas relative to a hardcoded constant; bounds prevent unbounded operator input.
- The context format is intentionally dual-mode. New parameter-aware Elements
must validate the extended encoding strictly, while legacy Elements continue
to receive their unchanged encoding.
- Toolkit and post-deployment verification must compare the compiled parameter,
not only the aggregate plan hash.
- PRs #90/#97 remain useful design history; this decision records the tested
implementation outcome rather than duplicating their proposed ADR file.

### Related Files

- `src/types/ComplianceTypes.sol`
- `src/registry/TokenPolicyRegistry.sol`
- `src/compliance/ComplianceEngine.sol`
- `src/compliance/elements/MinimumTradeAmount.sol`
- `src/compliance/recipes/MinimumTradeAmountRecipe.sol`
- `src/demo/BuidlLikeDemoAsset.sol`
- `services/toolkit/src/production-onboarding.ts`
- `services/cli/src/assetProfiles.ts`
- `docs/architecture/asset-manifest.md`
- `docs/product-specs/buidl-like-demo-profile.md`
51 changes: 51 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,57 @@
동시에 하나의 feature만 `active` 상태로 둔다.


## CORE-006 — Manifest-bound Element Parameters

### Behavior

- A Manifest may provide a bounded, unique `ElementParameter[]` keyed by an
Element's immutable `bytes32 elementId`. Parameters must belong to an Element
used by the Manifest, be non-empty and be at most 256 bytes each.
- `TokenPolicyRegistry` copies the parameter beside every compiled Element rule
that uses it and commits the aligned `bytes[]` into both the binding plan hash
and aggregate compiled plan hash. Parameter changes therefore use the existing
delayed semantic Manifest update path.
- Parameterized Elements receive `abi.encode(ComplianceContext, bytes)` while
manifests without parameters retain the exact legacy `abi.encode(ComplianceContext)`
call and binding hash. The stable `IComplianceElement` ABI is unchanged.
- The generic `MIN-TRADE-v1` Element and recipe enforce an injected inclusive
minimum regulated-asset quantity. Missing, malformed or zero parameters fail
closed with a detailed Element reason code.
- The BUIDL-like reference profile now composes independent Reg D 506(c),
3(c)(7) qualified-purchaser and minimum-trade recipes. Its existing
`5,000,000 ether` demo threshold is Manifest input rather than immutable
BUIDL-specific predicate bytecode.
- Legacy `BuidlMinimumInvestment` and `BuidlLikeFundRecipe` contracts remain
source/deployment compatibility artifacts, but new demo deployments do not
register or bind them.
- Toolkit production onboarding validates and exports Element parameters,
includes them in deterministic plan commitments, and verifies each compiled
parameter directly from the registry. CLI onboarding uses the parameter-aware
Factory entry point only for profiles that declare parameters.

### Verification

- Targeted Forge registry, Factory, generic minimum and BUIDL integration tests:
82 passed (79 CORE-006 cases plus 3 legacy BUIDL recipe compatibility cases)
- `npm test --prefix services/toolkit`: passed
- `npm test --prefix services/cli`: passed
- Full `forge test --offline`: 878/878 passed
- `scripts/e2e-anvil.sh --profile buidl-like`: 7/7 scenarios plus dashboard,
bidirectional RFQ, QP expiry/recovery and CLI settlement passed
- Isolated `/tmp` `scripts/check.sh`: passed after formatting only the
pre-existing `script/DeployProductionCore.s.sol` drift and installing clean
service dependencies in the copy
- Original-tree `scripts/check.sh` stops at the same pre-existing
`script/DeployProductionCore.s.sol` formatting drift; that unrelated file is
not modified by CORE-006
- Scoped `forge fmt` and `git diff --check`: passed

### State

passing


## CORE-005 — Compliance Core Production Hardening

### Behavior
Expand Down
23 changes: 23 additions & 0 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,29 @@ source of truth로 사용한다.

## Completed

- `CORE-006 — Manifest-bound Element Parameters`: Manifest registration and
delayed updates now accept bounded token-scoped `ElementParameter[]`, compile
the parameter alongside every referenced immutable Element rule, and commit
aligned parameter bytes into binding/aggregate plan hashes. Parameterless
manifests retain the exact legacy Element context and hash behavior. A generic
fail-closed `MIN-TRADE-v1` predicate and standalone recipe replace the active
BUIDL-specific minimum predicate; the BUIDL-like profile is now composed from
independent Reg D, 3(c)(7) QP and parameterized minimum-trade bindings while
legacy contracts remain compatibility-only. Factory, demo/testnet scripts,
CLI and production Toolkit support the new path; Toolkit verification compares
each compiled parameter directly with the expected Manifest input. This
preserves the existing demo's inclusive `5,000,000 ether` per-trade behavior
without claiming that the value represents BlackRock/Securitize production
policy or resolving subscription-vs-secondary-trade semantics. 검증: targeted
Forge 82/82 (CORE-006 79 + legacy compatibility 3), Toolkit/CLI smoke, full
`forge test --offline` 878/878, BUIDL-like
Anvil E2E 7/7 plus dashboard/bidirectional RFQ/QP-expiry/CLI settlement, scoped
formatting and `git diff --check` pass. An isolated `/tmp` full
`scripts/check.sh` passed after formatting only the pre-existing
`script/DeployProductionCore.s.sol` drift and installing clean dependencies.
Original-tree `scripts/check.sh` still stops at that unrelated formatting
drift, which CORE-006 deliberately does not include.

- `SDK-003 — Publishable Package Release Contract`: CLI, Toolkit과 RFQ SDK를
독립 npm tarball로 build/pack하고 Node 20 clean temporary projects에 설치하는
release gate를 완성했다. Toolkit packed export/config simulation, generated
Expand Down
12 changes: 11 additions & 1 deletion docs/architecture/asset-manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ onboarding may only strengthen enforcement (`FLAG_ONLY < OPERATOR_REVIEW <
BLOCK`); `FORCE_FLAG_ONLY` is accepted only when the Element default is already
`FLAG_ONLY`.

Token-scoped Element settings use bounded `ElementParameter[]` entries keyed by
immutable `elementId`. The registry rejects empty, duplicate, unused, oversized
(more than 256 bytes), or excessive parameter entries. Parameter bytes are copied
beside every compiled rule that references the Element and are included in that
binding's plan hash. Consequently a parameter change is a semantic Manifest
update: it follows the existing owner schedule/operator activation timelock,
increments Manifest history, and changes `compiledPlanHashOf(token)`. Existing
manifests without parameters retain their prior compiled-plan hash and legacy
Element context ABI.

`ManifestCore`의 과거 issuance/fund 필드는 ABI 전환을 위한 deprecated mirror이며
현재 Engine, Factory와 CLI의 source of truth는 registry의 `RecipeBinding[]`다.

Expand All @@ -110,7 +120,7 @@ hot path에 필요한 compact core만 온체인에 둔다. 법률 문서, 심사

- `ACTIVE`가 아닌 Manifest는 regulated execution을 허용하지 않는다.
- pair 거래에서 양쪽 자산의 classification과 regulated Manifest를 누락하지 않는다.
- Recipe key, version, compiled Element enforcement plan, engine과 scope가
- Recipe key, version, compiled Element enforcement/parameter plan, engine과 scope가
decision에 바인딩된다.
- full manifest hash가 변경되면 새로운 version 또는 명시적 update가 필요하다.
- ACTIVE/SUSPENDED core fact를 직접 덮어써 timelock을 우회할 수 없다.
Expand Down
7 changes: 7 additions & 0 deletions docs/architecture/compliance-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ swap, quote fill, order matching 또는 settlement 자체는 수행하지 않는

Element는 하나의 구성요건 사실만 판정한다. 특정 Recipe나 venue에 종속되지 않는다.

자산마다 달라지는 수치·목록은 Element bytecode 상수가 아니다. Manifest의 bounded
`ElementParameter[]`로 입력하고, registry가 immutable Element rule과 함께 compile해
plan hash에 커밋한다. 설정이 없는 기존 Element에는 과거와 같은 `abi.encode(ctx)`를,
설정된 Element에는 `abi.encode(ctx, parameterBytes)`를 전달해 ABI 호환성을 유지한다.

Element 추가 기준:

1. 기존 Element로 같은 사실을 표현할 수 없는가?
Expand Down Expand Up @@ -133,6 +138,8 @@ struct ComplianceDecision {
- Element default enforcement와 onboarding override는 registration/update 시점에
bounded compiled plan으로 고정한다. 일반 onboarding은 strengthen-only이며
`FORCE_FLAG_ONLY` downgrade는 허용하지 않는다.
- 자산별 Element parameter도 registration/update 시점에 bounded compiled plan으로
고정하며 parameter 변경은 Manifest timelock/history를 우회할 수 없다.
- Element가 nonzero reasonCode를 반환하면 Engine/CLI가 그 값을 그대로 전달한다.
zero reason만 recipe-scoped generic code `1`로 fallback한다.
- Asset Manifest가 기존 single Recipe mapping/Token Policy 역할을 확장한다.
Expand Down
10 changes: 8 additions & 2 deletions docs/compliance/recipes/R3_ICA-3c7-Fund.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ type: recipe-requirement-spec
recipe-id: R3
recipe-name: ICA §3(c)(7) Fund
project: RWA DEX (Giwa) · corner-store
status: v2.0 (2026-07-28) — 2부 구성(제1부 법률논증 산문 + 제2부 구현명세). Part II는 실장 컨트랙트(Fund3c7Recipe.sol · BuidlLikeFundRecipe.sol) 기준.
status: v2.1 (2026-09-08) — current runtime composes Fund3c7Recipe independently; BuidlLikeFundRecipe v1 is compatibility-only.
substance-sot: "승준 recipe walkthrough — R3_ICA-3c7-Fund.md v1.0 (2026-06-17, 조문별 삼단논법). 보경 recipe 검토본 없음 — 법률 검토 필요."
implements: "src/compliance/recipes/Fund3c7Recipe.sol (recipeId 2, {A-13}, fund-bit gated) · BuidlLikeFundRecipe.sol (recipeId 3, {A-13, BUIDL-MIN}, fund-bit gated)."
implements: "Current: Fund3c7Recipe.sol (recipeId 2, {A-13}, fund-bit gated). Compatibility only: BuidlLikeFundRecipe.sol v1. Commercial minimum is a separate MinimumTradeAmountRecipe v2 binding."
reflects-decisions: [ADR-004, ADR-006, ADR-008]
umbrella: "SPEC.md — 공유 개념(Element/Recipe/Manifest·Router cumulative AND·경계)은 여기에 의한다"
legal-effect: "발행자가 ICA상 investment company가 아님(§3(c)(7) 제외) → ICA 등록·실체규제 면제"
Expand All @@ -15,6 +15,12 @@ tags: [recipe-requirement-spec, R3, ica, 3c7, qualified-purchaser, always-on]

# R3 ICA §3(c)(7) Fund — 요구사항 명세서 (Recipe)

> **2026-09-08 조합 정정.** 현재 BUIDL-like 데모는 `Fund3c7Recipe`
> `{A-13}`를 독립 binding으로 사용한다. 최소 거래 수량은 별도
> `MinimumTradeAmountRecipe` `{MIN-TRADE-v1}`와 Manifest parameter로 조합한다.
> 아래의 `BuidlLikeFundRecipe` 설명은 이미 존재하는 v1 호환 기록이며 신규
> onboarding 또는 현재 데모의 권장 구성이 아니다.

> **저술 지위 고지.** 본 Recipe의 법적 논증은 승준 recipe walkthrough(2026-06-17)를 산문 2부 형식으로 재구성한 것이며, 대응 보경 recipe 검토본은 없다 — 법률 검토 전 상태(제4절). 제2부의 두 기준 컨트랙트(`Fund3c7Recipe.sol`, `BuidlLikeFundRecipe.sol`)는 모두 mock이며, 그 요소 집합은 법적 논증이 요구하는 이상 집합보다 축약되어 있다(제10절 seam).

본 문서는 컴플라이언스 **Recipe** R3(ICA §3(c)(7) 펀드)의 요구사항 명세서이다. **제1부**는 R3가 성립·유지시키는 법률효과 — "이 발행자는 1940년 투자회사법(ICA)상 investment company가 아니다(§3(c)(7) 제외)" — 의 근거와 조문별 도출을, **제2부**는 이를 구현한 컨트랙트 기준의 활성화·구성·거절 명세를 규정한다. R3는 발행·재판매를 불문하고 모든 이전에 상시 얹히는 누적(always-on cumulative) Recipe라는 점에서 R1·R2와 구조가 다르다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type: requirement-spec
project: RWA DEX (Giwa) · corner-store
element-id: BUIDL-MIN
element-name: Minimum Investment Threshold (최소 투자금 한도 · 데모 프로파일)
status: v0.1 (2026-07-28) — walkthrough 부재. 데모 프로파일 규칙(상업적 fund-terms 성격). 법적 성격 규명은 검토 필요.
status: superseded (2026-09-08) — deployed/source compatibility record only; current demo uses generic MIN-TRADE-v1 with Manifest-bound parameters.
substance-sot: "없음 — 본 명세가 1차 저술. 데모 발행 프로파일의 상업적 조건을 온체인 게이트로 모델링한 것."
implements: "src/compliance/elements/BuidlMinimumInvestment.sol (ELEMENT_ID BUIDL-MIN-v1, 커밋 'Add BUIDL-like minimum investment gate')"
reflects-decisions: [ADR-004(pool 신규 등재 필요), ADR-006]
Expand All @@ -15,6 +15,14 @@ tags: [requirement-spec, BUIDL-MIN, minimum-investment, demo-profile, new-elemen

# BUIDL-MIN Minimum Investment Threshold — 요구사항 명세서

> **2026-09-08 구현 정정.** 아래 문서는 이미 배포될 수 있었던 v1 동작의 역사적
> 기록으로 보존한다. 현재 데모는 BUIDL 전용 Element/Recipe를 활성화하지 않는다.
> `MIN-TRADE-v1`(generic predicate), `MinimumTradeAmountRecipe` v2, 그리고
> `BuidlLikeDemoAsset.elementParameters()`의 `5,000,000 ether` 설정을 조합한다.
> 설정값은 compiled plan hash에 포함되고 변경 시 Manifest timelock/history를 거친다.
> “subscription minimum” 여부는 아직 법률·상품 결정이 아니므로 현재 동작을
> **양방향 per-trade regulated-asset quantity minimum**으로 정확히 제한해 설명한다.

> **저술 지위·데모 성격 고지.** 본 부품은 대응 walkthrough가 없으며, 컨트랙트 `BuidlMinimumInvestment.sol`은 개발팀이 Giwa MVP 데모 프로파일용으로 선반영한 것이다. 컨트랙트 NatSpec이 명시하듯 **본 부품은 실제 BlackRock/Securitize BUIDL 토큰이 본 요소를 통해 연동될 수 있다는 주장을 하지 아니하며**, 공개된 5백만 달러 최소 투자금 사실을 1달러 순자산가치(NAV) 단위로 환산하여 테스트 발행 규칙으로 모델링한 것이다. 최소 투자금 한도는 본질적으로 발행·펀드 조건(상업적 term)으로서 특정 증권법 조문의 요건이 아니므로, **제1부는 그 상업적 조건의 후보 규범적 맥락을 1차 저술한 것**이며 법적 성격 규명은 검토를 요한다(제4절). 본 부품은 Element Pool Freeze v1(ADR-004)에 포함되지 아니하였으므로 신규 등재 절차를 요한다.

본 문서는 컴플라이언스 부품 BUIDL-MIN(최소 투자금 한도)의 요구사항 명세서이다. **제1부**는 본 부품이 강제하는 조건의 후보 맥락과 검토 쟁점을, **제2부**는 실장된 컨트랙트 `BuidlMinimumInvestment.sol`을 기준으로 한 구현 명세를 규정한다.
Expand Down
58 changes: 58 additions & 0 deletions docs/compliance/spec-sheets/MIN-TRADE_minimum-trade-amount.spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
type: requirement-spec
project: RWA DEX (Giwa) · corner-store
element-id: MIN-TRADE-v1
element-name: Minimum Trade Amount
status: v1.0 (2026-09-08) — technical predicate; commercial/legal semantics require asset-level approval
implements: src/compliance/elements/MinimumTradeAmount.sol
stateful: false
review-required: product, legal
---

# MIN-TRADE-v1 — Minimum Trade Amount

## Purpose

Provide one reusable threshold predicate for assets whose approved commercial
terms require a minimum regulated-asset quantity. The Element is asset-neutral:
the amount is supplied by the token Manifest, not compiled into bytecode.

## Configuration

`ElementParameter("MIN-TRADE-v1", abi.encode(uint256 minimumAmount))`

- exactly one ABI-encoded nonzero `uint256`
- registry input is public, bounded to 256 bytes, unique by `elementId`, and must
be used by a bound Recipe
- the bytes are included in the binding and aggregate compiled plan hashes
- changes use the normal delayed Manifest semantic-update lifecycle

## Evaluation

The Engine passes the regulated token amount (`amountOut` when the asset is
`tokenOut`, otherwise `amountIn`). v1 returns PASS iff `amount >= minimumAmount`.

| code | meaning |
|---:|---|
| 1 | parameter missing, malformed, or zero (fail closed) |
| 2 | regulated-asset trade amount is below the configured minimum |

## Composition

The Element's Recipe contains only `MIN-TRADE-v1`. Investor eligibility such as
Accredited Investor or Qualified Purchaser is expressed through separate Recipe
bindings. A BUIDL-like demo therefore composes:

1. Reg D 506(c)
2. ICA 3(c)(7)
3. Minimum Trade Amount

No BUIDL-specific logic exists below the demo Manifest/profile boundary.

## Scope warning

This version preserves the previous demo's per-trade, both-direction quantity
check. It does not assert that a real fund's published minimum means secondary
trade size, initial subscription, buy-only minimum, or post-trade balance. Those
semantics require a new reviewed Element/version and, where needed, NAV/oracle or
position state.
Loading
Loading