오늘 작업의 reference 스냅샷. 내일 S2.4 (SDK 전환) 진입 시 어떤 상태에서 출발하는지 파악하기 위함.
| 작업 | 상태 |
|---|---|
| S2.1 milestone (Foundry CTF cycle 테스트) | ✅ 통과 (11/11, 전체 28/28) |
| S2.1 — §7 open questions Q1-Q4 + Q6 + Q8 + Q9 답변 확인 | ✅ 측정/테스트로 confirmed |
| S2.1 — Q7 (Auto-claim delegate scope) | ⏳ S7로 deferred |
| S2.2 setup — DeployCTF.s.sol 동작 | ✅ anvil 배포 검증 완료 |
| Plan §1.4 / §2.2.7 / §4 — Oracle 3-stage progression 명시 | ✅ 갱신 |
| English 학습 형식을 영구 메모리화 | ✅ 글로벌 + 프로젝트 두 곳에 저장 |
원래 plan은 Gnosis CTF를 직접 vendor하려 했으나, Polymarket의 ctf-exchange 저장소 자체가 동일 문제를 이미 우아하게 해결했음을 발견:
artifacts/ConditionalTokens.json에 저장 → 0.8 테스트에서 raw bytecode를 CREATE로 배포forge install Polymarket/ctf-exchange로 submodule 추가, vm.parseJsonBytes(...)로 바이트코드 읽어 deploy결과: 0.5/0.8 컴파일러 호환성 이슈 zero. CTFExchange 통합도 같은 submodule에서 옴 (소스로 컴파일).
packages/contracts/test/CTFCycle.t.sol (260+ 줄, 11 tests):
| 테스트 | 답변하는 §7 question | 결과 |
|---|---|---|
test_FullCycle_YesWinsAndPaysFullCollateral |
(sanity) | ✅ |
test_LoserRedeemReturnsZero |
Q1 | ✅ revert 없이 0 반환 확인 |
test_SplitAfterResolveAllowed |
Q2 | ✅ resolve 후 split 가능 (의미 없지만 허용) |
test_SplitFromContractWithReceiver_Succeeds |
Q3 | ✅ 컨트랙트 caller의 receiver hook 발화 |
test_SplitFromContractWithoutReceiver_Reverts |
Q8 | ✅ receiver 미구현 시 revert (ERC-1155 spec) |
test_GasSnapshot_Split |
Q4 | ✅ 151k gas (EOA) |
test_GasSnapshot_Merge |
Q4 | ✅ 191k gas |
test_GasSnapshot_Redeem_BothIndexSets |
Q4 + Q9 | ✅ 240k gas |
test_GasSnapshot_Redeem_OnlyWinner |
Q4 + Q9 | ✅ 230k gas |
test_PrepareCondition_DoubleCallReverts |
Q6 | ✅ revert string "condition already prepared" 캡처 |
test_RedeemCombinedVsSeparate_Gas |
Q9 | ✅ combined 54k vs separate sum 82k (33% 차이) |
이게 “provisional answer + 테스트” 패턴의 진짜 가치를 입증한 모멘트:
Wrong 1: Position ID를 직접 keccak으로 계산
// WRONG — naive keccak
bytes32 collId = keccak256(abi.encodePacked(parentCollectionId, conditionId, indexSet));
uint256 posId = uint256(keccak256(abi.encodePacked(collateral, collId)));
실제로 CTHelpers는 EC arithmetic을 씀 (nested condition 위해). 결과: 내가 계산한 positionId와 CTF가 mint하는 token ID가 일치하지 않아 _balance1155가 0 반환. 테스트 실패.
수정: CTF의 자체 helper 사용
bytes32 collId = ctf.getCollectionId(parentCollectionId, conditionId, indexSet);
uint256 posId = ctf.getPositionId(IERC20(address(usdc)), collId);
Wrong 2: Stray IERC20.balanceOf(address) 호출
// WRONG — CTF는 ERC-1155, ERC-20 single-arg balanceOf 없음
assertEq(IERC20(address(ctf)).balanceOf(alice), 0, ...);
이전 작성 시 잘못 남긴 라인. CTF에 해당 메서드 없어서 EVM revert. 테스트 실패.
수정: 그 줄 제거. ERC-1155 balance는 별도 헬퍼 _balance1155(holder, tokenId)로 staticcall.
| 작업 | EOA caller | Contract caller (with hook) | 비교 |
|---|---|---|---|
splitPosition |
151k gas | 496k gas | hook 시 +345k |
mergePositions |
191k gas | (측정 안 함, hook fire 안 함) | — |
redeemPositions([1,2]) |
240k gas | — | 양쪽 다 burn |
redeemPositions([1]) only winner |
230k gas | — | 한쪽만 burn |
MM Agent 디자인 시사점:
claim() 래퍼는 사용자가 양쪽 다 가지고 있으면 combined redeem([1,2])를 default로 (separate보다 33% 저렴)packages/contracts/src/MockUSDC.sol) — 6 decimals, open mint, 80줄 minimal ERC-20vm.parseJsonBytes)new CTFExchange(usdc, ctf, address(0), address(0))). proxy/safe factories는 0으로 (계정 추상화 path 비활성화 — S7에 다시 활성화)=== v2 (CTF) backbone deployed ===
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
MockUSDC: 0x5FbDB2315678afecb367f032d93F642f64180aa3
ConditionalTokens: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
CTFExchange: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
(Anvil 기본 deterministic 주소 — 같은 anvil 인스턴스에서 redeploy 시 동일 주소.)
문제: CTFExchange.sol은 pragma solidity 0.8.15 (정확 버전 핀). 우리 컨트랙트는 ^0.8.24. 한 컴파일 단위에서 둘 다 충족할 단일 solc 버전 없음.
해법:
MockUSDC.sol과 script/DeployCTF.s.sol을 ^0.8.15로 (CTFExchange와 같은 컴파일 단위 공유)Market.sol, MarketFactory.sol (S1 scaffold)는 ^0.8.24 유지 — DeployCTF에서 import 안 하므로 별도 컴파일 단위foundry.toml에서 solc = "0.8.24" 핀 제거 → foundry가 파일별로 자동 선택fs_permissions에 lib/ctf-exchange/artifacts read 추가 (artifact JSON 읽기용)이전 §1.4 S6 row는 Chainlink와 UMA를 단일 항목 “Chainlink price feed 자동 resolve + UMA optimistic oracle 통합” 으로 lump했음. 사용자 명시 요청: manual → Chainlink → UMA 순서로 분명히 하라.
| 섹션 | 변경 |
|---|---|
| §1.4 S2 row | “Manual oracle (Stage 1 of 3)” 명시적 deliverable + milestone 추가. operator EOA가 prepareCondition + reportPayouts 직접 호출 |
| §1.4 S6 row | 단일 “Chainlink + UMA” deliverable → 두 개로 분리: “Chainlink adapter (Stage 2)” + “UMA adapter (Stage 3)”. Milestone 둘로 분리 (각 stage로 resolve된 마켓 한 개 이상) |
| §2.2.7 Oracle | 한 줄 “Chainlink Price Feed” → 3-stage 표 (Manual / Chainlink / UMA) + 각 stage의 사용 케이스 + 한계 + 채택 순서 이유 |
| §4 Phase 2 요약 | “Oracle — Chainlink + UMA” → “Oracle — 3-stage progression (manual S2 → Chainlink S6 → UMA S6 후반)” |
conditionId = keccak256(oracleAddress, questionId, outcomeSlotCount) — oracle 주소가 다르면 conditionId가 다름. 즉 stage 1에 manual oracle로 만든 마켓을 stage 2의 Chainlink adapter로 옮길 수 없음 (다른 conditionId, 다른 ERC-1155 토큰 ID).
해석: 이건 결함이 아니라 디자인 의도. 마켓은 짧은 수명 (며칠~몇 주), stage 도입 시 이미 있던 마켓은 자기 stage로 끝까지 운영하고, 새 마켓이 새 stage 사용. 마이그레이션 비용 zero.
신뢰 가정의 점진적 분산:
순서를 뒤집으면 (UMA부터 도입) 시스템이 너무 무거워서 S2의 핵심 가치 (빠른 통합 + 풀스택 한 바퀴 검증)를 놓침.
이번 세션의 답변 형식 (English check + bilingual + Today’s phrases + brief eval)을 두 곳에 저장:
| 파일 | 범위 | 내용 |
|---|---|---|
~/.claude/projects/-Users-jay-work/memory/feedback_english_learning_format.md |
Project — /Users/jay/work 전체 |
~80줄 spec (구조 템플릿, 규칙, 예외, 사용자 반복 패턴) |
~/.claude/CLAUDE.md |
Global — 머신 모든 Claude Code 세션 | ~50줄 leaner 버전, project memory 가리킴 |
미래 모든 Claude Code 세션에서 자동 로드. “evaluate my English” 다시 요청할 필요 없음. 두 파일 sync 유지 책임 명시 (한쪽 갱신 시 다른 쪽 mirror).
캡처된 사용자 반복 패턴 (eval에서 자주 짚는 것들):
X and Y and the Z 대신 X, Y, and Zctf-exchange branch (origin과 비교):
fdeece7 plan: oracle progression — manual (S2) → Chainlink (S6) → UMA (S6 후반)
6d8d920 S2.1 milestone: CTFCycle Foundry test (28/28 pass) + S2.2 deploy script
d0add3a plan/gnosis-ctf-research §7: provisional answers Q1-Q5, unify into one list
─────── (이번 commit) ───────
+ 2026-05-13 history.md entry
+ 2026-05-13-s2-implementation-and-oracle-progression.md (이 파일)
Not pushed yet — 사용자가 push 명령 시 git push origin ctf-exchange.
S2.4 = SDK 표면 전환. 기존 @verex/sdk의 escrow API (buyYes / buyNo / resolve / claim)를 CLOB API (fillOrder / fillOrders + signOrder EIP-712)로 전환.
S2.4는 mechanical 작업이 아니라 디자인 작업이라 시작 전 사용자 입력 필요:
signOrder 단독 vs createOrder + signOrder 분리?각 결정이 SDK 사용자 코드 모양에 영향을 줌.
packages/sdk/src/order.ts — order struct 타입 + signing 헬퍼packages/sdk/src/exchange.ts — createExchangeClient(...) 가 fillOrder, fillOrders, getOrderbook 등 wrappackages/sdk/scripts/sync-abis.mjs 갱신 — Polymarket의 CTFExchange ABI도 sync@verex/sdk의 factory.ts / market.ts (S1 의 v1 escrow API)는 deprecated mark + history note에서 유지verex order sign, verex order fill, verex split, verex merge, verex redeem 같은 명령 디자인S2.4 + S2.5 + S2.6 완료 시:
이게 S3 (Web MVP) 진입 조건.