크로스체인 실무 체크리스트
도입
크로스체인 기능은 "체인 하나 더 지원"하는 작업이 아니다. token address, asset type, route provider, finality 기준, bridge/message status, indexer, support copy, incident runbook을 함께 열어야 한다. 하나라도 빠지면 사용자에게는 같은 USDC처럼 보이지만 내부에서는 다른 자산, 다른 복구 경로, 다른 장애 조건이 섞인다.
이 강의는 Cross-chain/L2 트랙의 release gate다. 앞에서 배운 CCTP, bridge, L2 risk, DA/finality, roadmap, intent, CCIP/CCT를 하나의 Go/No-Go packet으로 묶는다. 목표는 새 체인이나 새 route를 추가할 때 "누가 어떤 근거로 승인했는가"를 남기는 것이다.
학습 목표
- chain/route 추가 결정을 asset, route, finality, security, operations 기준으로 검수한다.
- 장애 차단 조건과 route disable 조건을 미리 정한다.
- capstone checkout 시스템에 넣을 cross-chain risk register를 작성한다.
- 사용자 표시 문구와 내부 ledger 상태를 분리한다.
개념 설명
체인 추가 결정을 Go/No-Go 기준으로 정리한다.
route별 trust assumption이 분리되는가
New chain Go/No-Go packet 작성
체인 추가 기준을 만들었다.
1. Go/No-Go packet은 체크박스가 아니라 승인 문서다
| 섹션 | Go 기준 | No-Go 예시 |
|---|---|---|
| Asset | native stablecoin, bridged/wrapped, CCT, intent-routed 자산 구분 완료 | symbol만 같고 issuer/native 여부 불명확 |
| Route | CCTP, CCIP, bridge, intent, liquidity route가 저장됨 | route provider와 fallback 미정 |
| Finality | 금액별 confirmation policy가 있음 | sequencer accepted만으로 고액 settlement 처리 |
| Security | replay, double claim, wrong recipient, timeout 테스트 있음 | message ID와 idempotency key 없음 |
| Operations | RPC fallback, indexer backfill, status source, alert owner 지정 | 장애 때 볼 source가 explorer 하나뿐 |
| Support | 사용자 pending/delayed/refund 문구 준비 | "전송 실패" 하나로 모든 상태 표시 |
승인 문서에는 담당자와 증거 링크가 있어야 한다. "확인함"이 아니라 source URL, 접근일, contract address, chain ID, domain ID, explorer link, test result, dashboard panel을 붙인다.
2. Asset checklist
| 질문 | 증거 |
|---|---|
| native stablecoin인가, bridged/wrapped인가 | issuer 공식 chain/token address 문서 |
| 같은 symbol을 같은 asset으로 취급하지 않는가 | asset registry에 canonical asset ID와 display label 분리 |
| token decimals/name/symbol이 indexer와 ledger에 반영됐는가 | token metadata fixture |
| token issuer 또는 token owner 권한이 문서화됐는가 | owner/admin multisig, timelock, event monitor |
3. Route checklist
| 질문 | 증거 |
|---|---|
| same-chain transfer와 cross-chain transfer를 DB에서 분리하는가 | route type enum |
| CCTP, CCIP, bridge, intent, liquidity routing 중 무엇인가 | route provider field |
| source/destination chain ID와 domain ID를 저장하는가 | payment row, event schema |
| fee와 예상 대기 시간을 사용자에게 보여주는가 | quote response, UI copy |
| token delivery와 message delivery가 둘 다 필요한가 | completion condition table |
4. Finality checklist
| 질문 | 증거 |
|---|---|
| wallet displayed, sequencer accepted, L2 finalized, L1 posted, bridge final을 구분하는가 | confirmation policy matrix |
| 고액 결제는 더 강한 기준을 쓰는가 | amount threshold table |
| service delivery와 settlement ledger를 분리하는가 | payment state machine |
| stuck/manual review 상태가 있는가 | status enum과 support macro |
5. Security and operations checklist
| 영역 | 필수 항목 |
|---|---|
| replay/double claim | message ID, nonce, authorization ID, payment ID uniqueness |
| recipient safety | destination address validation, receiver selector allowlist |
| timeout | attestation timeout, solver timeout, receiver retry timeout |
| upgrade/pause | bridge contract, token pool, router, receiver pause/upgrade monitoring |
| provider | RPC fallback, explorer fallback, CCIP/CCTP status source |
| indexing | backfill, reorg handling, failed handler retry |
| treasury | rebalancing policy, emergency withdrawal, route exposure limit |
| incident | route disable, refund decision, user copy, postmortem owner |
코드로 확인하기
위 신뢰 모델을 코드와 운영 규칙으로 확인한다. 메시지 상태, 최종성, 재시도, 관측 지점이 어디서 분리되는지 보는 것이 목적이다.
운영Cross-chain route 정의 YAML — Go/No-Go 패킷의 단일 원천
한 route(
source-chain + destination-chain + protocol) 마다 yaml 한 항목. release gate에 그대로 입력.
# cross-chain-routes.yamlroutes: - id: base-to-mainnet-cctp sourceChainId: 8453 destinationChainId: 1 protocol: cctp-v2 assetClass: usdc-native finality: source: 200 # Base blocks attestation: "60s p50, 300s p99" destination: "block confirmation 1" fee: sourceGasUsd: 0.05 attestationApiUsd: 0 destinationGasUsd: 2.5 maxFeeBps: 30 securityReview: tokenAdministratorAudit: passed poolPauseAuthority: "circle-multisig" lastVerifiedAt: 2026-05-14 auditUrl: "https://www.circle.com/security/cctp-v2" operationalReadiness: monitoringDashboard: "https://grafana.internal/cctp" alertRoutes: ["#payments-oncall"] runbook: "runbooks/cctp-stuck.md" manualReviewQueue: configured goNoGo: verdict: "go" conditions: - "tx 한도: 100k USDC per route per hour" - "attestation p99 SLA 5 분 위반 30분 지속 시 route disable" approver: "risk-lead" expires: 2026-08-14백엔드Route 활성화 게이트 — CI에서 강제
yaml에 정의된 라우트만 코드에서 사용 가능. 미정의 chain 조합은 런타임에 거절.
import { readFileSync } from "node:fs";import { parse } from "yaml";type RouteDef = { id: string; sourceChainId: number; destinationChainId: number; protocol: "cctp-v2" | "ccip" | "erc-7683-intent" | "lock-mint-bridge"; assetClass: string; goNoGo: { verdict: "go" | "limited" | "no-go"; expires?: string };};const ROUTES: RouteDef[] = (parse(readFileSync("cross-chain-routes.yaml", "utf8")) as any).routes;export function findActiveRoute(args: { sourceChainId: number; destinationChainId: number; protocol: RouteDef["protocol"];}): RouteDef | null { const r = ROUTES.find( (x) => x.sourceChainId === args.sourceChainId && x.destinationChainId === args.destinationChainId && x.protocol === args.protocol ); if (!r) return null; if (r.goNoGo.verdict === "no-go") return null; if (r.goNoGo.expires && new Date(r.goNoGo.expires) < new Date()) return null; return r;}export function assertRouteSupported(args: { sourceChainId: number; destinationChainId: number; protocol: RouteDef["protocol"] }) { const r = findActiveRoute(args); if (!r) { throw new Error(`route not active: ${args.sourceChainId} -> ${args.destinationChainId} via ${args.protocol}`); } return r;}운영Route disable trigger — alert 임계값 위반 시 자동 정지
SLA 위반·attestation outage·token pool pause 같은 신호가 N분 이상 지속되면 자동으로 route를 일시 정지하고 oncall에 통지.
type RouteHealthSample = { routeId: string; observedAt: Date; signal: "attestation_p99_ms" | "indexer_lag_seconds" | "destination_revert_rate" | "token_pool_paused"; value: number;};const THRESHOLDS = { attestation_p99_ms: 300_000, indexer_lag_seconds: 600, destination_revert_rate: 0.05, token_pool_paused: 1};export async function evaluateRouteHealth(samples: RouteHealthSample[]) { const breaches = samples.filter( (s) => s.value > THRESHOLDS[s.signal] ); if (breaches.length === 0) return { action: "keep-open" }; const persisted = breaches.length >= 6; // 5분 간격 * 6 = 30분 if (persisted) { await prisma.route.update({ where: { id: breaches[0].routeId }, data: { temporarilyDisabled: true, disabledReason: breaches[0].signal, disabledAt: new Date() } }); await pageOncall({ severity: "critical", reason: `route auto-disabled: ${breaches[0].routeId} (${breaches[0].signal})` }); return { action: "auto-disable" }; } return { action: "warn" };}강의 포인트
| 관점 | 수업 중 확인할 질문 | 산출물 |
|---|---|---|
| 승인 | 새 route를 누가 어떤 evidence로 승인하는가 | Go/No-Go packet |
| 장애 | 어떤 alert가 route disable로 이어지는가 | route disable runbook |
| 상태 | 사용자 상태와 internal ledger 상태가 분리됐는가 | status mapping table |
| 캡스톤 | checkout capstone에 어떤 risk를 넣을 것인가 | cross-chain risk register |
| 운영 | 재검토 주기와 source owner가 있는가 | review cadence |
실무 예시
운영Route disable runbook template
| Trigger | 즉시 조치 | 사용자 표시 | 복구 조건 |
|---|---|---|---|
| sequencer down | 신규 checkout route disable | 이 네트워크 결제가 지연 중입니다 | sequencer live + pending queue 정상화 |
| CCTP attestation delay | cross-chain payout hold | cross-chain 확인을 기다리는 중입니다 | attestation received + destination mint 가능 |
| CCIP receiver failure | programmable route hold | 수동 확인 중입니다 | manual execution 또는 compensation 완료 |
| indexer lag | completion display freeze | 결제 확인이 지연되고 있습니다 | backfill complete + reconciliation pass |
| wrapped asset incident | asset route remove | 해당 자산 지원 일시 중단 | issuer/bridge status + risk approval |
Capstone risk register 예시
| Risk | Owner | Evidence | Alert | Fallback | User copy |
|---|---|---|---|---|---|
| wrong asset type | Product + Protocol | asset registry | unsupported token submitted | reject route | 지원되지 않는 자산입니다 |
| attestation delay | Cross-chain owner | CCTP message status | SLA exceeded | manual review | cross-chain 확인 중입니다 |
| receiver revert | Protocol owner | CCIP message status + tx log | execution failed | manual execution | 수동 확인 중입니다 |
| sequencer outage | SRE | L2 status + RPC errors | route health critical | route disable | 네트워크 지연 중입니다 |
| indexer lag | Data platform | chain head vs indexed head | lag threshold | backfill | 결제 확인이 지연됩니다 |
| duplicate claim | Security | nonce/payment ID check | duplicate event | block settlement | 중복 결제 확인 중입니다 |
| fee spike | Payments | quote + blob/route fee | fee threshold | raise min or disable | 수수료가 일시적으로 높습니다 |
| bridge upgrade | Security | upgrade event | unapproved upgrade | pause route | 해당 경로 점검 중입니다 |
흔한 오해와 실패 시나리오
| 오해 | 실제 기준 |
|---|---|
| token address만 있으면 chain support가 끝난다 | issuer support, route, finality, operations까지 필요하다 |
| bridge나 CCIP explorer가 있으면 자체 DB가 덜 중요하다 | product ledger와 external status를 reconcile해야 한다 |
| pending 상태는 예외다 | cross-chain에서는 pending, delayed, manual review가 정상 운영 상태다 |
| route disable은 장애 후에 정하면 된다 | trigger, owner, user copy를 사전에 정해야 한다 |
| capstone에는 성공 flow만 넣으면 된다 | risk register와 incident runbook이 capstone 품질을 결정한다 |
실습 과제
- 운영New chain Go/No-Go packet 작성: 새 L2 또는 cross-chain route를 추가한다고 가정하고 asset, route, finality, security, operations, support, monitoring 항목을 승인 패킷으로 작성한다.
- 운영Route disable runbook 작성: sequencer down, CCTP attestation delay, CCIP receiver failure, indexer lag, wrapped asset incident 중 하나를 골라 route disable과 사용자 안내 절차를 작성한다.
- 운영Capstone risk register 연결: stablecoin checkout capstone에 들어갈 cross-chain risk 8개를 선정하고 owner, evidence, alert, fallback, user copy를 지정한다.
완료 기준
- asset, route, finality, security, operations, support 기준이 포함된 Go/No-Go packet을 작성했다.
- route disable trigger와 복구 조건을 정했다.
- 사용자 상태와 internal ledger 상태를 분리했다.
- capstone checkout risk register에 cross-chain risk 8개를 연결했다.
근거 자료
- 크로스체인 실무 체크리스트: 03-크로스체인-L2/05-크로스체인-실무-체크리스트.md
- Circle CCTP Technical Guide: https://developers.circle.com/cctp/references/technical-guide
- Chainlink CCIP Documentation: https://docs.chain.link/ccip
- L2BEAT Risk Analysis: https://l2beat.com/scaling/risk
- Ethereum.org Data Availability: https://ethereum.org/developers/docs/data-availability/
- The Graph Indexing Overview: https://thegraph.com/docs/en/indexing/overview/
- ERC-7683: Cross Chain Intents: https://eips.ethereum.org/EIPS/eip-7683