Skip to content
Merged
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
29 changes: 29 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,31 @@ passing
- Non-goals: partial fill, orderbook, production pricing engine, dealer inventory,
custody 확장, websocket/order discovery.

## DEMO-001 — BUIDL-like ERC-3643 Demo Asset

### Behavior

- `src/demo/BuidlLikeDemoAsset.sol`이 Giwa MVP용 BUIDL-like asset profile을 제공한다.
- 테스트 fixture가 profile의 이름/심볼로 실제 ERC-3643/T-REX 토큰을 배포한다.
- profile Manifest는 Reg D 506(c) 발행 Recipe와 ICA 3(c)(7) fund Recipe를 동시에 바인딩한다.
- `factsPacked` fund bit가 켜진 자산에서는 BUIDL-like fund Recipe가 A-13 Qualified Purchaser와 minimum investment 검사를 활성화한다.
- BUIDL-like flow test는 실제 Securitize/TA 연결 대신 `MockSecuritizeTA` fixture로 investor facts를 만들고, 그 결과를 ERC-3643 registry와 Corner Store Elements에 sync한다.
- protected Router 경로에서 QP buyer는 체결되고, accredited-only/non-QP buyer는 토큰 이동 전 거절된다.
- sanctioned QP buyer는 token movement 전에 compliance reject된다.
- QP buyer라도 BUIDL-like minimum investment 미만이면 token movement 전에 compliance reject된다.
- expired TA profile은 current eligibility로 sync되지 않고 token movement 전에 compliance reject된다.
- engine-level AI/QP flags가 통과해도 ERC-3643-unverified recipient는 settlement에서 rollback된다.
- QP/규제 로직은 BUIDL 전용 토큰 override가 아니라 Manifest/Recipe/Element에 남긴다.
- DS Protocol/Securitize-style Compliance Service는 profile 문서에서 adapter seam으로 매핑한다.
- 현실 BlackRock BUIDL 연동, Securitize claim 연동, NAV/redemption/distribution rail은 범위 밖이다.

### Verification

- `forge fmt --check src/demo/BuidlLikeDemoAsset.sol test/fixtures/TREXSuite.sol test/fixtures/MockSecuritizeTA.sol test/integration/IntegrationBase.sol test/integration/BUIDLLikeFlow.t.sol src/compliance/elements/BuidlMinimumInvestment.sol src/compliance/recipes/BuidlLikeFundRecipe.sol test/unit/compliance/BuidlLikeFundRecipe.t.sol`
- `forge test --offline --match-path test/integration/BUIDLLikeFlow.t.sol -vv`
- `forge test --offline --match-path test/unit/compliance/BuidlLikeFundRecipe.t.sol -vv`
- `forge test --offline`

## RFQ-002 — RFQ v2 Hardening

### Behavior
Expand Down Expand Up @@ -213,6 +238,10 @@ passing

### Notes

- 데모 문서: `docs/compliance/07-buidl-implementation.md`
- Profile spec: `docs/product-specs/buidl-like-demo-profile.md`
- 현재 구현은 local BUIDL-like profile + fixture다. 실제 BUIDL 온보딩은 법률 확정, issuer/trusted-claim 연동, 1차/2차 rail 분리 후 별도 feature로 진행한다.

- 정책 결정: D009(9-element in-place 확장, operator-gated setter, Lockup은
fixture-only mock acquisition source).
- Deferred follow-up: ungated legacy mock element(A-01/A-03/QP)의 operator-gate
Expand Down
58 changes: 22 additions & 36 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ source of truth로 사용한다.
- `DOC-001 — Imported Architecture Alignment`
- `FND-001 — Foundry Product Foundation`
- `RFQ-001 — Reference RFQ Settlement`
- `DEMO-001 — BUIDL-like ERC-3643 Demo Asset`
- `RFQ-002 — RFQ v2 Hardening`
- `CMP-001 — Reg D 506(c) 9-element Recipe`(illustrative element library + recipe
9-element 확장, version 2)
Expand Down Expand Up @@ -68,8 +69,7 @@ source of truth로 사용한다.

1. MVP RFQ demo backend milestone/user flow를 별도 문서·feature로 구체화한다.
기존 live-Anvil E2E/CLI 경로를 재사용한다.
2. pending RFQ/E2E/CLI/BUIDL PR stack(#35)이 머지된 뒤
roadmap과 feature 상태를 재조정한다.
2. pending PR stack 정리 후 roadmap과 feature 상태를 재조정한다.
3. 남은 RFQ production policy를 별도 feature로 분리한다: custody, partial fill,
production dealer/operator 책임.
4. acquisition/lot data source와 holding-period Recipe 활성화 조건을 결정한다
Expand All @@ -80,39 +80,25 @@ source of truth로 사용한다.

## Last Session Summary

- #30 merge preparation includes `CLI-002` (corner-store CLI v2) on top of current main. src/·script/ 변경 없이 `services/cli`에
명령 7개(check/sell/balances/watch/faucet/snapshot/restore/quote-inspect)를 더했다.
- #35 merge preparation includes `DEMO-001 — BUIDL-like ERC-3643 Demo Asset` on top of current main.
- 변경한 파일:
- CLI: `services/cli/src/`(abi/config/rfq/commands/index), `services/cli/test/smoke.ts`,
`services/cli/README.md`
- docs/bookkeeping: `docs/demo.md`, `FEATURES.md`(CLI-002), `PROGRESS.md`
- 설계 요점:
- `check`는 active manifest의 recipe → `requiredElements()` → element `check()`를
eth_call로 돌려 per-element PASS/FAIL을 만들고 `engine.evaluate`(view)로 verdict를
낸다. 엔진이 `ctx.buyer`만 스크리닝하므로 subject 무시 원소(B-01/B-02/E-01)를
`[asset-side]`로 표기. FAIL reason은 recipe-aware code로 디코딩(엔진이 낼 코드와 동일).
- `sell`은 `SwapFlow.t.sol::test_sell_shaped_success`의 컨텍스트(ctx.buyer=매도자,
tokenIn=RWA/tokenOut=QUOTE, venueData=zeroForOne=false)를 그대로 미러링.
- `watch`는 `eth_getLogs`(address+topic0 OR 필터) 폴링. reasonCode는 reason-table →
recipe-0 monitoring-flag(`encode(0,elementId,1)`, SurveillanceFlag) → bytes32 라벨
순으로 디코딩; ManifestStatusChanged status·SurveillanceFlag elementId도 사람이 읽게.
- EIP-712 복구는 services/rfq lib의 `domain`+`RFQ_QUOTE_TYPES`로 `verifyTypedData`
(타입 문자열 재선언 없음). ABI는 계속 hand-written fragment(out/ 비의존).
- 실행한 명령:
- `npm run build` / `npm test`(services/cli)
- `scripts/e2e-anvil.sh --keep` + 전체 CLI v2 walkthrough(account 4)
- `forge test --offline`
- 통과한 검증:
- smoke ok: quote-inspect 서명자 복구(valid=maker / tampered≠maker) + reason 회귀.
- live walkthrough: fresh `check`(A-02/A-03/A-04/C-01 FAIL, exit 1) → setup+kyc →
`check`(전 PASS) → faucet → buy(+100) → sell 40(RWA-40/QUOTE+40) → balances 전후 →
snapshot(0x11) → attest ZZ → `check`(A-02 하나 FAIL, exit 1) → restore → `check`(green)
→ watch --from 0(세션 이벤트 디코딩 재생) → rfq-quote → quote-inspect(PASS) → 변조 →
quote-inspect(signature FAIL, exit 1).
- `forge test --offline` 238/238 유지(Solidity 변경 없음).
- `src/demo/BuidlLikeDemoAsset.sol`
- `src/compliance/elements/BuidlMinimumInvestment.sol`
- `src/compliance/recipes/BuidlLikeFundRecipe.sol`
- `test/fixtures/MockSecuritizeTA.sol`
- `test/integration/IntegrationBase.sol`
- `test/integration/BUIDLLikeFlow.t.sol`
- `docs/product-specs/buidl-like-demo-profile.md`
- `FEATURES.md`, `PROGRESS.md`
- 완료한 작업:
- BUIDL-like ERC-3643 demo asset profile 추가
- Mock Securitize/TA fixture로 investor facts를 주입하고 Element flags로 sync
- QP/minimum investment/sanctions/expiry/ERC-3643 recipient verification 시나리오 추가
- 최신 Manifest lifecycle에 맞춰 BUIDL-like manifest도 register→approve flow로 정합화
- 실행한 검증:
- original PR CI/checks passed before retarget
- conflict reconciliation checked with `git diff --check`
- 남은 리스크:
- manifest는 데모가 recipe 7(Reg D + Surveillance, 10 원소)로 onboarding한 상태라
`check`가 10개 원소를 매긴다(recipe 1이면 9개). 재배포 시 recipe에 따라 달라짐.
- `snapshot`/`restore`는 anvil 전용. 셸에서 id 캡처 시 `snapshot id:` 라인만 파싱할 것
(도움말의 "snapshot ids" 문구가 느슨한 grep에 걸릴 수 있음).
- amount는 ether 단위(18 decimals) 가정. 나머지 스택 리스크는 CLI-001/E2E-001과 동일.
- 실제 BlackRock BUIDL/Securitize/TA 연결은 구현 범위가 아니다.
- AI/QP는 아직 production ONCHAINID claim이 아니라 mock TA에서 sync되는 test flag다.
- NAV, redemption rail, monthly distribution, production claim issuer 연동은 별도 feature다.
135 changes: 135 additions & 0 deletions docs/product-specs/buidl-like-demo-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# BUIDL-like ERC-3643 Demo Profile

## Status

Current demo profile; not real BlackRock/Securitize BUIDL integration.

## Purpose

This profile lets the Giwa MVP demonstrate how a BUIDL-like regulated fund asset can trade through Corner Store's protected Router path using the existing ERC-3643/T-REX fixture and the Corner Store Manifest/Recipe/Element model.

The test model is an ERC20-style BUIDL reference asset converted into a local ERC-3643/T-REX test issuance. It is not a bridge, wrapper, or live integration with the real BUIDL token.

The goal is to prove the product flow, not to claim production compatibility with the real BUIDL token.

External transfer-agent integration is also modeled, not connected. The test
suite uses a mock Securitize/TA-style fixture as the source of investor facts,
then syncs those facts into the local ERC-3643/T-REX identity registry and
Corner Store Elements.

## Current implementation

- Demo profile: `src/demo/BuidlLikeDemoAsset.sol`
- Mock TA fact source: `test/fixtures/MockSecuritizeTA.sol`
- Integration fixture: `test/integration/BUIDLLikeFlow.t.sol`
- ERC-3643/T-REX harness: `test/fixtures/TREXSuite.sol`

The demo deploys a standard ERC-3643/T-REX token with BUIDL-like metadata:

- token name: `BUIDL-like ERC-3643 Demo Asset`
- token symbol: `bBUIDL`
- issuance recipe: Reg D 506(c)
- fund recipe: BUIDL-like ICA 3(c)(7) recipe
- fund applicability: `factsPacked` bit 0
- minimum investment amount: `5,000,000` demo units, modeled as $5M at $1 NAV
- supported execution engine: AMM in the current fixture

## Compliance model

The current profile intentionally keeps BUIDL-specific behavior out of token override code.

```text
BUIDL-like asset facts
-> ManifestCore
-> Recipe binding
-> Element checks
-> Router-protected execution
-> ERC-3643 transfer-time verification
```

Current demo checks:

- KYC/identity verification through the ERC-3643/T-REX fixture
- sanctions clear through `A-01-v1`
- accredited investor through `A-03-v1`
- qualified purchaser through `A-13-v1`
- BUIDL-like minimum investment through `BUIDL-MIN-v1`
- ERC-3643 recipient verification at token transfer time

Investor facts are seeded through `MockSecuritizeTA`, not by calling the AI/QP
Elements directly from the BUIDL flow test. This keeps the demo shaped like the
production seam without pretending to have a real Securitize or transfer-agent
connection.

```text
MockSecuritizeTA profile
-> sync into ERC-3643 identity registry when KYC is current
-> sync AI/QP/sanctions flags into test Elements
-> Router evaluate()
-> ERC-3643 transfer-time verification
```

## DS Protocol / Securitize mapping

Public Securitize DS Protocol material describes a Compliance Service style boundary around token operations, including validation / pre-transfer checks. Corner Store should not replace its ERC-3643 identity model with DS-specific internals. The integration boundary should be an adapter.

| DS / TA concept | Corner Store demo equivalent |
| --- | --- |
| DSToken / permissioned token | ERC-3643/T-REX token fixture |
| Compliance Service | Corner Store `ComplianceEngine` + ERC-3643 transfer-time checks |
| pre-transfer validation | Router `evaluate()` before adapter execution + token `canTransfer` during settlement |
| Wallet / whitelist manager | ERC-3643 `IdentityRegistry` + ONCHAINID claims |
| TA / trusted verification source | `MockSecuritizeTA` in tests; future ERC-3643 `TrustedIssuersRegistry` / TrustedIssuer integration |
| investor eligibility facts | mock TA profile now; claim topics consumed by Elements in production |
| audit / reliance context | decision hash, reason code, events, future reliance log |

## Claim topics

The demo follows the PD-4 direction: use ERC-3643-compatible claim topics and a TrustedIssuer-style pipeline rather than inventing a separate identity model.

Initial project-level topics used by the profile:

- `KYC = keccak256("CORNER_STORE.KYC")` in the T-REX fixture
- `ACCREDITED_INVESTOR = 1001`
- `QUALIFIED_PURCHASER = 1002`

The current AI/QP Elements still use settable test flags, but BUIDL-like flow
tests populate those flags through `MockSecuritizeTA`. Production refinement
should replace those flags with ONCHAINID claims carrying issuer, topic,
issuedAt, expiresAt, and revocation/freshness semantics.

## Demo acceptance cases

The BUIDL-like profile should demonstrate:

1. metadata and Manifest binding are correct
2. QP buyer can buy through the protected Router
3. accredited but non-QP buyer is rejected before token movement
4. sanctioned QP buyer is rejected before token movement
5. QP/accredited but ERC-3643-unverified recipient rolls back during token settlement
6. QP/accredited buyer below the BUIDL-like minimum investment is rejected before token movement
7. expired TA profile is not synced into current eligibility and is rejected before token movement

## Non-goals

- real BlackRock BUIDL deployment compatibility
- real Securitize/TA API connection
- real Securitize issuer address / topic mapping
- production claim expiry and revocation integration
- NAV oracle integration
- redemption rail implementation
- monthly distribution rail implementation
- production transfer-agent agreement or legal reliance opinion

## Follow-up work

- Implement ONCHAINID-backed AI/QP claim fixtures.
- Add claim expiry/freshness tests.
- Add Securitize/DS adapter research issue once official/current integration details are available.
- Split primary distribution, secondary DEX execution, redemption, and monthly distribution rails.
- Promote BUIDL-like profile data into the future Manifest compiler/onboarding flow.

## References

- Securitize DS Protocol Compliance Service article: https://medium.com/securitize/ds-protocol-the-compliance-service-b6fe472d625d
- User-provided verified/source browser reference for BUIDL-like DSToken inspection: https://vscode.blockscan.com/ethereum/0x603bb6909be14f83282e03632280d91be7fb83b2
1 change: 1 addition & 0 deletions docs/product-specs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
| [`../architecture/asset-manifest.md`](../architecture/asset-manifest.md) | Asset Compliance Manifest 책임과 lifecycle | Current |
| [`../ROADMAP.md`](../ROADMAP.md) | 구현 순서와 완료 조건 | Current |
| [`../MVP.md`](../MVP.md) | 과거 AMM 중심 설계 기록 | Historical |
| [`buidl-like-demo-profile.md`](buidl-like-demo-profile.md) | Giwa MVP용 BUIDL-like ERC-3643 demo asset profile | Current |
| [`rfq-backend-sdk-and-demo.md`](./rfq-backend-sdk-and-demo.md) | RFQ backend SDK와 MVP demo backend 계획 | Current |

DOC-001에서 연구·회의 입력을 반영했다. 내용이 다를 경우 위 current 문서를 source
Expand Down
1 change: 0 additions & 1 deletion services/cli/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,3 @@ function summarize(name: string, args: any[]): DecodedRevert {

// Thrown by commands to signal a clean nonzero exit with a message.
export class CliError extends Error {}

47 changes: 47 additions & 0 deletions src/compliance/elements/BuidlMinimumInvestment.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import {BaseElement} from "./BaseElement.sol";
import {
ElementMetadata,
ElementCategory,
TemporalNature,
Decidability,
ObligationTiming,
Statefulness
} from "../../types/ComplianceTypes.sol";
import {ReasonCodes} from "../../libraries/ReasonCodes.sol";

/// @dev BUIDL-like demo minimum investment threshold.
///
/// This is a test-issuance rule for the Giwa MVP profile, not a claim that the
/// live BlackRock/Securitize BUIDL token can be integrated through this element.
/// The engine passes the regulated asset quantity as `amount`; in this demo the
/// BUIDL-like unit is treated as a $1 NAV share, so 5,000,000 tokens models the
/// public $5M minimum-investment fact documented in the product spec.
contract BuidlMinimumInvestment is BaseElement {
bytes32 internal constant ELEMENT_ID = "BUIDL-MIN-v1";
uint256 public constant MINIMUM_AMOUNT = 5_000_000 ether;

constructor()
BaseElement(ElementMetadata({
elementId: ELEMENT_ID,
category: ElementCategory.ASSET_ATTRIBUTE,
version: "BUIDL-MIN-v1",
temporal: TemporalNature.REALTIME,
decidability: Decidability.DETERMINISTIC,
timing: ObligationTiming.AT_TRADE_GATE,
statefulness: Statefulness.STATELESS
}))
{}

function check(address, address, address, uint256 amount, bytes calldata)
external
pure
override
returns (bool passed, bytes32 reasonCode)
{
passed = amount >= MINIMUM_AMOUNT;
reasonCode = passed ? bytes32(0) : ReasonCodes.encode(0, ELEMENT_ID, 1);
}
}
22 changes: 22 additions & 0 deletions src/compliance/recipes/BuidlLikeFundRecipe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import {BaseRecipe} from "./BaseRecipe.sol";

/// @dev BUIDL-like fund recipe for the local ERC-3643 test issuance profile.
/// Requires QP plus the demo minimum-investment Element. Conditionally
/// applicable when Manifest.factsPacked bit0 marks the asset as a fund.
contract BuidlLikeFundRecipe is BaseRecipe {
constructor() BaseRecipe(3, 1, _elementsBuidlLikeFund()) {}

function _elementsBuidlLikeFund() private pure returns (bytes32[] memory e) {
e = new bytes32[](2);
e[0] = "A-13-v1";
e[1] = "BUIDL-MIN-v1";
}

function isApplicable(bytes calldata context) external pure override returns (bool) {
uint256 factsPacked = abi.decode(context, (uint256));
return (factsPacked & 1) == 1;
}
}
51 changes: 51 additions & 0 deletions src/demo/BuidlLikeDemoAsset.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import {ManifestCore, PolicyStatus} from "../types/ComplianceTypes.sol";

/// @title BuidlLikeDemoAsset
/// @notice Giwa MVP demo profile for a local BUIDL-like ERC-3643 asset.
///
/// This intentionally does NOT subclass or fork the ERC-3643 token. The token
/// remains the standard T-REX Token; this profile packages the asset-specific
/// metadata and Manifest binding that make the demo asset BUIDL-like.
///
/// Keep compliance rules in Manifest/Recipe/Element, not in a one-off token
/// override. That is the product point: asset onboarding is configuration plus
/// policy binding, not bespoke transfer logic per asset.
library BuidlLikeDemoAsset {
string internal constant TOKEN_NAME = "BUIDL-like ERC-3643 Demo Asset";
string internal constant TOKEN_SYMBOL = "bBUIDL";

uint16 internal constant ISSUANCE_RECIPE_ID = 1; // Reg D 506(c)
uint16 internal constant ISSUANCE_RECIPE_VERSION = 1;
uint16 internal constant FUND_RECIPE_ID = 3; // BUIDL-like ICA 3(c)(7) + minimum investment
uint16 internal constant FUND_RECIPE_VERSION = 1;

bytes32 internal constant PROFILE_KEY = keccak256("CORNER_STORE.PROFILE.BUIDL_LIKE_DEMO_V1");
bytes32 internal constant SECURITIZE_DS_ADAPTER_SEAM = keccak256("CORNER_STORE.ADAPTER.SECURITIZE_DS_PROTOCOL");
uint256 internal constant CLAIM_TOPIC_ACCREDITED_INVESTOR = 1001;
uint256 internal constant CLAIM_TOPIC_QUALIFIED_PURCHASER = 1002;
uint256 internal constant MINIMUM_INVESTMENT_AMOUNT = 5_000_000 ether;

// Current skeleton convention: factsPacked bit0 means the fund recipe is applicable.
uint256 internal constant FACT_FUND_APPLICABLE = 1;

function manifest(uint8 supportedEngines) internal pure returns (ManifestCore memory m) {
m.status = PolicyStatus.ACTIVE;
m.issuanceRecipeId = ISSUANCE_RECIPE_ID;
m.issuanceRecipeVersion = ISSUANCE_RECIPE_VERSION;
m.fundRecipeId = FUND_RECIPE_ID;
m.supportedEngines = supportedEngines;
m.factsPacked = FACT_FUND_APPLICABLE;
m.fullManifestHash = keccak256(
abi.encode(
PROFILE_KEY,
SECURITIZE_DS_ADAPTER_SEAM,
CLAIM_TOPIC_ACCREDITED_INVESTOR,
CLAIM_TOPIC_QUALIFIED_PURCHASER,
MINIMUM_INVESTMENT_AMOUNT
)
);
}
}
Loading
Loading