캡스톤: Stablecoin Checkout 시스템 설계
도입
캡스톤은 앞선 강의를 요약하는 과제가 아니다. 학습자가 하나의 stablecoin checkout 시스템을 설계하고, 그 설계가 왜 출시 가능한지 또는 왜 아직 출시하면 안 되는지 증거로 설명하는 최종 산출물이다.
좋은 캡스톤 문서는 기능 목록이 아니라 판단 기록이다. 어떤 token과 chain을 지원할지, permit과 ERC-3009 중 무엇을 쓸지, cross-chain route를 어떤 조건에서 열지, invariant와 monitoring으로 무엇을 검증할지, 사고가 나면 누가 어떤 권한으로 멈출지까지 연결해야 한다.
학습 목표
- permit/ERC-3009, invariant, CCTP/L2, reconciliation, risk dashboard를 하나의 설계 문서로 연결한다.
- 출시 전 Go/No-Go 판단과 잔여 위험을 문서화한다.
개념 설명
Adopt
Trial
Assess
Hold
최종 산출물은 "Stablecoin Checkout Design Packet"이다. 아래 10개 섹션을 모두 채운다.
| 섹션 | 반드시 포함할 내용 | 근거가 되는 수업 |
|---|---|---|
| 1. Scope | 지원 token, chain, route, merchant, 결제 금액 범위 | token support, cross-chain checklist |
| 2. Architecture | frontend, API, relayer, contract, indexer, dashboard | labs 전체 |
| 3. Payment Methods | permit, ERC-3009, x402를 언제 쓰는지 | signed payment, x402 lab |
| 4. State Machine | invoice/payment/cross-chain 상태와 단방향 전이 | checkout, escrow, CCTP lab |
| 5. Data Model | invoiceId, paymentId, resourceId, nonce, transferId, receipt | labs evidence |
| 6. Security Controls | role, pause, freeze, signature, replay, external call | security checklist |
| 7. Tests and Invariants | unit, fuzz, invariant, integration, fork test 계획 | invariant lab |
| 8. Reconciliation | onchain event, DB ledger, explorer, issuer status 비교 | indexing/dashboard lessons |
| 9. Operations | monitoring, alert, incident runbook, key rotation, upgrade/rollback | monitoring runbook |
| 10. Go/No-Go | release blocker, residual risk, owner, mitigation due date | final review |
추천 아키텍처는 아래처럼 한 번에 볼 수 있어야 한다.
코드로 확인하기
앞에서 만든 설계를 실습 코드로 연결한다. 예시는 그대로 외우는 대상이 아니라, 구현 파일에서 어떤 줄을 읽고 어떤 테스트를 붙일지 정하는 기준이다.
캡스톤은 6개 랩의 코드를 통합하는 설계 문서다. 아래 두 스니펫은 그 통합 지점을 나타내는 최소한의 코드 흔적이다.
백엔드통합 데이터 모델 (TypeScript)
invoice / payment / transfer / receipt를 한 상태 머신으로 통합. 같은 entity가 여러 lab의 산출물을 갖고 있어 각 lab의 식별자(
invoiceId,paymentId,transferId,nonce)가 모두 연결되어야 한다.
export type ProductStatus = | "Created" | "Signed" | "Submitted" | "Paid" | "PendingAttestation" | "Delivered" | "Settled" | "Refunded" | "Failed";export type CheckoutRecord = { productId: string; // 사용자 화면에서 보이는 id invoiceId?: `0x${string}`; // permit checkout lab paymentId?: `0x${string}`; // ERC-3009 escrow lab transferId?: `0x${string}`; // CCTP simulator lab resourceId?: `0x${string}`; // x402 server lab nonce?: `0x${string}`; // ERC-3009 / x402 replay 방지 status: ProductStatus; payer: `0x${string}`; merchant: `0x${string}`; amount: bigint; sourceChainId: number; destinationChainId: number; evidence: { typedDataHash?: `0x${string}`; transferTxHash?: `0x${string}`; attestationStatus?: "pending" | "delayed" | "attested" | "failed"; deliveryRef?: string; settlementTxHash?: `0x${string}`; refundTxHash?: `0x${string}`; };};인덱서정합성 검증 의사코드
onchain 이벤트와 내부 ledger를 합산해 invariant 두 가지를 강제: 총 청구액 일치, status 단방향 전이. capstone 패킷의 reconciliation 섹션이 이 검증을 기반으로 한다.
import { z } from "zod";const Transition: Record<ProductStatus, ProductStatus[]> = { Created: ["Signed", "Failed"], Signed: ["Submitted", "Failed"], Submitted: ["Paid", "Failed"], Paid: ["PendingAttestation", "Delivered", "Refunded", "Failed"], PendingAttestation: ["Delivered", "Failed"], Delivered: ["Settled", "Refunded"], Settled: [], Refunded: [], Failed: []};export function assertReconciled(records: CheckoutRecord[], onchainTotal: bigint) { const productTotal = records .filter((r) => r.status === "Settled" || r.status === "Delivered") .reduce((sum, r) => sum + r.amount, 0n); if (productTotal !== onchainTotal) { throw new Error(`ledger mismatch: product ${productTotal} vs onchain ${onchainTotal}`); }}export function assertSingleStepTransition(prev: ProductStatus, next: ProductStatus) { if (!Transition[prev].includes(next)) { throw new Error(`forbidden transition ${prev} -> ${next}`); }}결제 상태는 최소한 아래 상태를 가진다.
| 제품 상태 | 의미 | 전환 증거 |
|---|---|---|
Created | invoice 또는 resource payment가 생성됨 | invoiceId/resourceId |
Signed | 사용자가 permit, authorization, payment payload에 서명함 | typed data hash, deadline/valid window |
Submitted | relayer 또는 client가 onchain 호출 제출 | transaction hash |
Paid | token movement가 성공함 | Transfer event, receipt |
PendingAttestation | source burn 이후 destination mint 대기 | CCTP transferId, attestation status |
Delivered | 서비스 또는 상품 제공 완료 | delivery event/API log |
Settled | merchant 정산 완료 | settlement event/ledger entry |
Refunded | payer 환불 완료 | refund event/ledger entry |
Failed | 자동 완료 불가 | reason, owner, next action |
강의 포인트
| 관점 | 확인할 질문 | 증거로 남길 것 |
|---|---|---|
| token support | native, bridged, wrapped asset을 구분했는가 | chainId, token address, issuer support |
| signed payment | permit과 ERC-3009 선택 기준이 명확한가 | method decision table |
| state machine | 자금 이동, 서비스 제공, 정산, 환불이 분리되어 있는가 | 상태 전이표 |
| security | freeze, pause, replay, role, external call이 테스트되는가 | security checklist 결과 |
| cross-chain | finality, attestation, stuck state, manual review가 있는가 | route Go/No-Go packet |
| operations | dashboard alert와 runbook owner가 지정되어 있는가 | incident table |
실무 예시
운영capstone의 핵심 표는 Go/No-Go 판단표다. 아래 형식으로 작성한다.
| 영역 | Go 기준 | No-Go 조건 | Owner |
|---|---|---|---|
| Token | allowlisted native USDC만 지원, decimals 검증 완료 | wrapped token을 native로 취급하거나 token address 출처가 불명확함 | protocol lead |
| Signature | domain, nonce, deadline, valid window 테스트 통과 | wrong spender/recipient 또는 replay 테스트 없음 | smart contract reviewer |
| Payment State | double pay, double refund, service failure 상태가 테스트됨 | Paid와 Delivered가 같은 상태로 묶임 | backend lead |
| Cross-chain | route별 finality와 pending 상태 문구가 있음 | burn 후 mint 전 상태를 사용자에게 숨김 | payments lead |
| Monitoring | transfer, attestation, indexer lag alert가 있음 | incident owner와 pause 권한이 불명확함 | ops lead |
최종 문서에는 "출시 가능"이라는 결론만 쓰지 않는다. 출시 가능하면 남은 위험과 운영 owner를 적고, 출시 불가라면 차단 조건과 해소 조건을 적는다.
흔한 오해와 실패 시나리오
| 오해 | 실제로 확인할 것 |
|---|---|
| 캡스톤은 앞 강의 내용을 붙이면 된다고 본다. | 붙여넣기가 아니라 하나의 checkout 시스템 설계로 재조합해야 한다. |
| 기능이 많을수록 좋은 설계라고 본다. | MVP route와 차단 route를 분리하고, 운영 가능한 범위만 Go로 판단한다. |
| payment success를 service success와 같게 본다. | 자금 이동, 서비스 제공, 정산, 환불은 서로 다른 증거를 가진다. |
| cross-chain 지원을 마케팅 문구로만 다룬다. | finality, attestation, stuck transfer, manual review를 설계해야 한다. |
| risk dashboard를 나중에 만든다고 둔다. | 출시 판단에는 alert, owner, runbook이 포함되어야 한다. |
실습 과제
- 백엔드캡스톤 설계 문서: 위 10개 섹션을 포함해 Stablecoin Checkout Design Packet을 작성한다.
- 컨트랙트결제 방식 선택표: permit, ERC-3009 escrow, x402, CCTP route를 사용 조건, 장점, 실패 상태, 테스트 증거로 비교한다.
- 운영Go/No-Go 검토: 출시 차단 조건, 잔여 위험, 운영 owner, 해소 기한을 표로 작성한다.
- 인덱서증거 연결: Mock token, Permit checkout, ERC-3009 escrow, invariant, CCTP simulator, x402 lab 결과가 설계 문서의 어떤 섹션에 들어가는지 mapping한다.
완료 기준
- 통합 설계 문서가 완성됐다.
- 근거와 실습 결과가 연결됐다.
- 출시 차단 조건과 잔여 위험이 명확하다.
근거 자료
- 스테이블코인 실무 체크리스트: 01-스테이블코인/07-스테이블코인-실무-체크리스트.md
- 보안리뷰 체크리스트: 02-보안-테스트/06-보안리뷰-체크리스트.md
- 크로스체인 실무 체크리스트: 03-크로스체인-L2/05-크로스체인-실무-체크리스트.md
- 실습 로드맵: 08-실습/00-실습-로드맵.md