← Index
Source: docs/history/2026-05-27-s2.4-sdk-cli-migration.md (auto-generated by scripts/generate-docs-html.mjs — edit the .md, not this file)

S2.4 — SDK + CLI migration to CTF stack (2026-05-27)

v1 escrow API(Market / MarketFactory)를 SDK·CLI에서 완전 제거하고, CTF stack(MockUSDC + ConditionalTokens + CTFExchange) 기반의 새 surface로 교체. 핵심 risk였던 off-chain EIP-712 reconstruction은 forge로 생성한 golden digest와 vitest로 parity 검증. anvil 위 E2E 데모도 통과.


1. 결과 요약

작업 상태
@verex/sdk v1 (factory.ts / market.ts) 제거
@verex/sdk CTF surface 추가 (orders, conditions, ct, exchange, usdc, clients)
sync-abis.mjs — CTFExchange / IConditionalTokens / MockUSDC 동기화
순수 off-chain hashOrderCTFExchange.hashOrder() parity 테스트 ✅ vitest 3/3
signOrder + EIP-712 signature recovery roundtrip
@verex/cli 재작성 — 10개 CTF 커맨드 + 데모 스크립트
anvil 위 E2E 데모 실행 (deploy → setup → sign+fill → resolve → redeem) ✅ alice 100 USDC → 140 USDC (+40)
forge test 회귀 검증 ✅ 34/34 통과
docs/plan/watch-list.md 신규 — Glamsterdam BAL 친화 설계 후보 항목 등록

S2 milestone 진행:


2. SDK 신규 구조

packages/sdk/
├─ scripts/sync-abis.mjs       ← CONTRACTS = ["CTFExchange", "IConditionalTokens", "MockUSDC"]
├─ src/
│  ├─ index.ts                 ← curated re-exports
│  ├─ types.ts                 ← Order, Side, SignatureType, OrderDomain, ClientConfig, OrderSigner
│  ├─ orders.ts                ← signOrder + hashOrder (순수 off-chain EIP-712)
│  ├─ conditions.ts            ← getConditionId (encodePacked + keccak)
│  ├─ ct.ts                    ← splitBinaryPosition, mergeBinaryPosition, redeemPositions,
│  │                              prepareCondition, reportPayouts, getBinaryPositionIds,
│  │                              balanceOf1155, setApprovalForAll, payoutDenominator,
│  │                              getOutcomeSlotCount, getCollectionId, getPositionId
│  ├─ exchange.ts              ← fillOrder, registerToken, addOperator, cancelOrder,
│  │                              hashOrderViaContract, getDomainSeparator
│  ├─ usdc.ts                  ← mint, approve, balanceOf, allowance
│  ├─ clients.ts               ← createCTClient, createExchangeClient, createUsdcClient
│  └─ abis/                    ← auto-generated, .gitignored
└─ test/
   └─ orders.test.ts           ← 3 vitest tests (parity + sign roundtrip + raw-pk signer)

2.1 핵심 설계 결정 (사후 정리)

2.2 EIP-712 parity 검증 (S2.4의 핵심 risk 해소)

// packages/contracts/script/EmitOrderHash.s.sol
// 결정적 Order 하나에 대해 exchange.hashOrder() 결과를 stdout에 emit.
// 출력값을 그대로 SDK test에 박아넣어 golden value 비교.
// packages/sdk/test/orders.test.ts
const EXPECTED_DIGEST = "0x68d8d9bd3897ca4fc1977b681d787f0da781b37f9f91c0b0b7a0fe7292571f93";
const EXCHANGE_ADDRESS = "0xf13D09eD3cbdD1C930d4de74808de1f33B6b3D4f";
expect(hashOrder(ORDER, DOMAIN)).toBe(EXPECTED_DIGEST);  // ✅

이 한 줄이 SDK의 도메인 reconstruction(name: "Polymarket CTF Exchange", version: "1", chainId, verifyingContract) + Order 타입 인코딩이 OpenZeppelin EIP712 mixin 결과와 byte-for-byte 일치함을 증명. Order 스키마 / 도메인 / 타입 순서 중 어느 하나만 어긋나도 즉시 fail.

추가 검증: signOrderrecoverTypedDataAddress → maker 주소 일치 (cryptographic side도 통과).

Golden digest 재생성:

cd packages/contracts
forge script script/EmitOrderHash.s.sol
# 출력의 chainId / exchange address / digest를 test/orders.test.ts에 반영

3. CLI 신규 커맨드

verex condition --oracle <addr> [--question <bytes32>] [--slots <n>]
verex balance   --oracle <addr> [--question <bytes32>] [--account <i>]
verex setup     [--question <bytes32>] [--mint <units>] [--account <i>]
verex resolve   --yes <n> --no <n> [--question <bytes32>] [--account <i>]
verex split     --condition <bytes32> --amount <units> [--account <i>]
verex merge     --condition <bytes32> --amount <units> [--account <i>]
verex redeem    --condition <bytes32> [--side yes|no|both] [--account <i>]
verex mint      --amount <units> [--to <addr>] [--account <i>]
verex order sign --token <id> --maker-amount <n> --taker-amount <n> [--side buy|sell] [--out <file>]
verex order fill --order <file> --amount <units> [--account <i>]

모든 커맨드는 USDC_ADDR / CTF_ADDR / EXCHANGE_ADDR 환경변수로 주소를 가져오고, --usdc / --ctf / --exchange 플래그로 override 가능 — DemoMarket.s.sol convention과 호환.

demo.ts — anvil에서 단일 명령으로 deploy → setup → BUY 서명/체결 → resolve → redeem 전체 lifecycle 실행. Polymarket의 manual oracle flow(Stage 1)와 SDK signature path 모두 한 번에 exercise.


4. 검증 방법 (How to verify)

4.1 SDK + 컨트랙트 회귀

# (1) SDK 빌드 + 타입체크
pnpm --filter @verex/sdk build       # tsc clean, dist/ regenerate

# (2) SDK 단위 테스트 (vitest)
pnpm --filter @verex/sdk test        # 3 tests pass
  # hashOrder matches golden digest
  # signOrder roundtrip recovers maker address
  # signOrder accepts raw private-key signer

# (3) CLI 빌드
pnpm --filter @verex/cli build       # tsc clean

# (4) 컨트랙트 회귀
cd packages/contracts && forge test  # 34/34 pass (변경 없음)

4.2 anvil 위 E2E 데모

# 별도 터미널
anvil

# CLI 데모
pnpm --filter @verex/cli build
pnpm --filter @verex/cli demo

기대 출력 (요지):

[1] deploying CTF backbone via forge script...
    USDC      0x5FbD...
    CTF       0xe7f1...
    Exchange  0x9fE4...
[2] preparing condition...
    conditionId=0x235274e4...
    YES id=52923191629941...
    NO  id=19941161922911...
[3] alice signing BUY order: 60 USDC -> 100 YES (price=$0.60/YES)
    on-chain digest: 0x8c44237e...
[4] operator filling alice's BUY order (full 60 USDC)...
    alice now holds: 100000000 YES + 40000000 USDC
[5] reporting YES wins (manual oracle, Stage 1)...
[6] alice redeems (YES only — cheapest path)...
    alice final USDC: 140000000  ← +40 USDC 수익
✓ CTF end-to-end demo complete

수학 검증: alice 100 USDC 시작 → BUY로 60 지출(40 잔여) → YES 100개 → 해결 후 redeem 100 USDC → 최종 140 USDC.

4.3 사용자가 직접 확인하면 좋을 항목


5. 명시적으로 deferred한 것 (다음 슬라이스)

  1. @verex/api의 broken VerexClient import 정리 — S2.4 이전부터 깨져있던 stub. 한 줄짜리 placeholder로 교체하거나, API를 CTF 기반으로 부분 마이그레이션 시작.
  2. MM Agent v0 (S2.5) — paper-trading minimum maker. matchOrders model 선호 (Q-S2.3.2 추천). 새 패키지 packages/mm-agent 생성부터.
  3. fee 정책 결정 (Q-S2.3.3)feeRateBps 런칭 시 0인지 nonzero인지. SDK는 양쪽 다 지원하지만 protocol-wide 정책은 미정.
  4. Smart-account signing (POLY_PROXY / POLY_GNOSIS_SAFE) — S7 AA work.
  5. getCollectionId / getPositionId 순수 off-chain 구현 — 현재는 CT 컨트랙트 호출로 우회 (Gnosis CTHelpers EC arithmetic 때문). MM Agent에서 hot path가 되면 BN254 EC 라이브러리 도입 검토.
  6. docs/plan/watch-list.md — Glamsterdam BAL 친화 설계 항목 등록 (트리거 발생 시 결정).

6. Open Questions (이전 슬라이스에서 이월)


7. 다음 세션 시작 시 권장 reading order

  1. 이 doc (5분) — 무엇을 했고 무엇이 남았는지
  2. packages/sdk/src/orders.ts (3분) — EIP-712 도메인·타입 정의가 한 파일에 모여 있어 가장 빠른 entry
  3. packages/sdk/test/orders.test.ts (3분) — golden digest 검증 패턴
  4. packages/cli/src/demo.ts (5분) — SDK 전 surface가 어떻게 조합되는지 한눈에
  5. packages/contracts/script/EmitOrderHash.s.sol (2분) — golden 재생성 절차 이해
  6. (옵션) docs/plan/watch-list.md — 미래 트리거 인박스 구조

8. 변경 파일

신규

수정

삭제