SettleLab
전체 코스
LESSON 05Stablecoin Systems

CCTP와 USDC 크로스체인 결제

심화45분근거 5

학습 결과

  • CCTP의 burn/mint와 브릿지 락업 모델을 구분한다.
  • 메시지 attestation 지연을 결제 UX에 반영한다.
  • Fast Transfer와 Standard Transfer의 finality, fee, allowance 차이를 제품 정책으로 분리한다.

선행 조건

  • 결제 상태머신과 정산 원장

완료 기준

  • burn/mint 모델을 설명한다.
  • attestation 대기 UX를 설계했다.
  • 체인별 리스크 메모를 작성했다.

CCTP와 USDC 크로스체인 결제

도입

CCTP를 bridge 목록의 하나로만 보면 중요한 차이를 놓친다. Circle의 CCTP는 source chain에서 USDC를 burn하고 destination chain에서 native USDC를 mint하는 방식이다. 일반 lock-and-mint bridge처럼 wrapped token을 발행하거나 liquidity pool에서 선지급하는 모델과 다르다.

제품 관점에서 핵심은 "한 번의 전송"처럼 보이는 사용자 경험이 실제로는 burn, finality, attestation, destination mint, fee/allowance 확인, 재시도 상태로 나뉜다는 점이다. 결제 시스템은 이 중간 상태를 숨기지 말고 사용자가 이해할 수 있는 언어로 표현해야 한다.

이 강의는 CCTP의 burn/mint 모델을 상태머신으로 만들고, Fast Transfer와 Standard Transfer를 제품 정책으로 나누는 법을 다룬다. 2026-05-14 기준 Circle 문서에서는 CCTP V2가 Fast Transfer, Hooks, 개선된 API를 포함하며, CCTP V1은 2026년 7월부터 10개월에 걸쳐 phase-out될 예정이라고 안내한다. 새 설계는 V2 문서를 기준으로 둔다.

여기서는 USDC 결제 제품 안에서 CCTP 상태를 어떻게 보여줄지에 집중한다. 브릿지와의 신뢰 모델 비교는 CCTP와 브릿지 신뢰 모델, 구현 연습은 CCTP 시뮬레이터 랩에서 이어진다.

학습 목표

  • CCTP의 burn/mint와 브릿지 락업 모델을 구분한다.
  • 메시지 attestation 지연을 결제 UX에 반영한다.
  • Fast Transfer와 Standard Transfer의 finality, fee, allowance 차이를 제품 정책으로 분리한다.

개념 설명

구조 맵가로 스크롤 · 크게 보기 지원
CCTP와 USDC 크로스체인 결제 구조 맵이 시각화는 stablecoin checkout과 정산 시스템에서 actor, 권한, 데이터 증거가 어느 레이어에서 갈라지는지를 보여주며, 'CCTP와 USDC 크로스체인 결제'에서 남겨야 할 설계 증거를 좁힌다.
01

핵심 개념

  • CCTP의 burn/mint와 브릿지 락업 모델을 구분한다.
  • 메시지 attestation 지연을 결제 UX에 반영한다.
02

검증 지점

  • 발행/상환과 결제 상태가 분리되는가
  • 이벤트와 내부 원장이 대조되는가
  • burn/mint 모델을 설명한다.
03

실습 산출물

  • CCTP 상태와 증거 정리하기
  • Fast/Standard Transfer 정책 작성하기
  • 페그·유동성 위험이 표시되는가
크게 보기
01

핵심 개념

  • CCTP의 burn/mint와 브릿지 락업 모델을 구분한다.
  • 메시지 attestation 지연을 결제 UX에 반영한다.
02

검증 지점

  • 발행/상환과 결제 상태가 분리되는가
  • 이벤트와 내부 원장이 대조되는가
  • burn/mint 모델을 설명한다.
03

실습 산출물

  • CCTP 상태와 증거 정리하기
  • Fast/Standard Transfer 정책 작성하기
  • 페그·유동성 위험이 표시되는가

1. CCTP는 native USDC burn-and-mint 흐름이다

Circle 문서는 CCTP를 native USDC를 여러 블록체인 사이에서 이동시키는 permissionless onchain utility로 설명한다. 사용자는 source chain에서 USDC를 burn하고, Circle Attestation Service가 burn message를 확인해 서명한 뒤, destination chain에서 그 attestation을 제출해 USDC를 mint한다.

흐름도가로 스크롤 · 크게 보기 지원
강의 흐름도상태, 책임, 검증 지점을 순서대로 읽기 위한 다이어그램이다.
크게 보기

이 흐름에서 "USDC가 이동했다"는 말은 정확히는 source에서 burn되고 destination에서 새로 mint됐다는 뜻이다. 이 차이 때문에 destination asset은 wrapped token이 아니라 native USDC가 된다. 대신 Circle attestation, supported domain, destination contract, finality 정책을 제품 설계에 넣어야 한다.

2. CCTP와 bridge의 신뢰 경계가 다르다

표 자료가로 스크롤 · 크게 보기 지원
항목CCTP burn/mintLock-and-mint bridgeLiquidity bridge
destination assetnative USDCwrapped/bridged tokennative 또는 wrapped
liquidity pool 필요없음bridge custody 필요필요
핵심 신뢰Circle attestation, supported domainbridge contract/custodianLP/solver/liquidity
실패 상태burn 후 attestation/mint 지연bridge exploit, wrapped depegliquidity 부족, fee 변동
사용자 설명"USDC를 소각 후 다른 체인에서 새로 발행""예치 후 IOU 수령""유동성 제공자가 선지급"
크게 보기
항목CCTP burn/mintLock-and-mint bridgeLiquidity bridge
destination assetnative USDCwrapped/bridged tokennative 또는 wrapped
liquidity pool 필요없음bridge custody 필요필요
핵심 신뢰Circle attestation, supported domainbridge contract/custodianLP/solver/liquidity
실패 상태burn 후 attestation/mint 지연bridge exploit, wrapped depegliquidity 부족, fee 변동
사용자 설명"USDC를 소각 후 다른 체인에서 새로 발행""예치 후 IOU 수령""유동성 제공자가 선지급"

이 표를 제품 문구로 바꾸면 bridge와 CCTP를 같은 문장으로 설명하면 안 된다. CCTP는 liquidity pool 부족을 걱정하는 모델이 아니라 attestation, finality, destination mint 상태를 관리하는 모델이다. 반대로 lock-and-mint bridge는 wrapped asset과 custody risk를 설명해야 한다.

3. Fast Transfer와 Standard Transfer를 정책으로 나눈다

CCTP V2에서는 속도와 finality 기준을 설계자가 선택해야 한다. Circle 문서 기준으로 Fast Transfer는 source chain burn이 hard finality에 도달하기 전에 더 빠르게 attestation을 받을 수 있지만 Fast Transfer allowance와 fee가 걸린다. Standard Transfer는 더 느리지만 hard finality 기반으로 간다.

표 자료가로 스크롤 · 크게 보기 지원
항목Fast TransferStandard Transfer
제품 목표빠른 사용자 경험비용과 finality 안정성 우선
attestation 기준soft/confirmed finality 후 빠른 attestationhard/finalized finality 후 attestation
allowanceFast Transfer allowance를 소비allowance를 소비하지 않음
feeFast Transfer fee가 route별로 달라질 수 있음Circle 문서의 Standard Transfer fee 정책 확인 필요
실패/대기 UXallowance 부족, fee 변동, soft finality 리스크긴 대기 시간, finality 지연
사용자 문구"빠른 전송, 수수료와 용량 제한 가능""느리지만 finality 확인 후 진행"
크게 보기
항목Fast TransferStandard Transfer
제품 목표빠른 사용자 경험비용과 finality 안정성 우선
attestation 기준soft/confirmed finality 후 빠른 attestationhard/finalized finality 후 attestation
allowanceFast Transfer allowance를 소비allowance를 소비하지 않음
feeFast Transfer fee가 route별로 달라질 수 있음Circle 문서의 Standard Transfer fee 정책 확인 필요
실패/대기 UXallowance 부족, fee 변동, soft finality 리스크긴 대기 시간, finality 지연
사용자 문구"빠른 전송, 수수료와 용량 제한 가능""느리지만 finality 확인 후 진행"

Fast Transfer allowance가 부족하면 기다리거나 Standard Transfer로 전환해야 한다. 따라서 checkout 제품은 "cross-chain 전송 중" 하나가 아니라 AllowanceInsufficient, AttestationPending, MintSubmitted 같은 상태를 가져야 한다.

4. 제품 상태는 source와 destination을 모두 저장한다

표 자료가로 스크롤 · 크게 보기 지원
상태사용자 설명시스템 처리
BurnSubmittedsource chain에서 전송 시작source tx 모니터링
BurnObservedburn message가 관찰됨message hash 저장
FinalityPendingfinality 기준 도달 대기Fast/Standard 정책 적용
AttestationPendingCircle attestation 대기API polling, timeout policy
AttestationReadydestination mint 증명 준비signed attestation 저장
MintSubmitteddestination mint 제출confirmation 대기
Minteddestination USDC 수령완료
AllowanceInsufficientFast Transfer 용량 부족대기 또는 Standard 전환
Delayed중간 단계 지연retry/manual support
크게 보기
상태사용자 설명시스템 처리
BurnSubmittedsource chain에서 전송 시작source tx 모니터링
BurnObservedburn message가 관찰됨message hash 저장
FinalityPendingfinality 기준 도달 대기Fast/Standard 정책 적용
AttestationPendingCircle attestation 대기API polling, timeout policy
AttestationReadydestination mint 증명 준비signed attestation 저장
MintSubmitteddestination mint 제출confirmation 대기
Minteddestination USDC 수령완료
AllowanceInsufficientFast Transfer 용량 부족대기 또는 Standard 전환
Delayed중간 단계 지연retry/manual support

상태마다 저장할 데이터도 다르다. Source tx hash, source domain ID, destination domain ID, burn message hash, attestation response, destination tx hash, fee, allowance snapshot, finality threshold를 분리해서 저장해야 한다.

5. 개발 체크리스트

  • source/destination domain ID를 정확히 매핑한다.
  • source와 destination의 token messenger/message transmitter 주소를 확인한다.
  • 같은 burn message가 double mint되지 않도록 message hash를 저장한다.
  • attestation API 장애를 제품 상태로 표현한다.
  • fast transfer를 쓰는 경우 fee, finality, liquidity tradeoff를 문서화한다.
  • CCTP와 일반 bridge를 혼동하지 않도록 UI copy를 분리한다.
  • CCTP V1 legacy와 V2 contract/API 차이를 설계 메모에 남긴다.
  • 지원 체인과 domain 목록은 Circle 공식 문서 기준으로 확인하고 마지막 확인일을 저장한다.
  • hooks를 사용할 때 destination execution 실패와 USDC mint 성공을 분리한다.

코드로 확인하기

위 설계 결정을 코드에서 확인한다. 상태, 서명, 원장 이동, 실패 처리가 어디에 놓이는지 따라 읽으면 앞의 모델이 실제 구현 경계로 내려온다.

컨트랙트CCTP TokenMessenger 인터페이스 — depositForBurn 호출

사용자(또는 결제 서버)가 source chain의 TokenMessenger.depositForBurn을 호출해 USDC를 소각하고, destination domain·recipient를 메시지에 박는다. message hash는 attestation의 키가 된다.

CODE SURFACEsolidity
interface ITokenMessenger {    function depositForBurn(        uint256 amount,        uint32 destinationDomain,        bytes32 mintRecipient,        address burnToken    ) external returns (uint64 nonce);    function depositForBurnWithCaller(        uint256 amount,        uint32 destinationDomain,        bytes32 mintRecipient,        address burnToken,        bytes32 destinationCaller    ) external returns (uint64 nonce);}interface IMessageTransmitter {    function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool);}contract CrossChainCheckout {    ITokenMessenger public tokenMessenger;    function payToRemoteMerchant(        uint256 amount,        uint32 destinationDomain,        bytes32 mintRecipient,  // 32바이트로 패딩된 destination merchant 주소        address usdc    ) external returns (uint64 nonce) {        IERC20(usdc).transferFrom(msg.sender, address(this), amount);        IERC20(usdc).approve(address(tokenMessenger), amount);        nonce = tokenMessenger.depositForBurn(amount, destinationDomain, mintRecipient, usdc);        // nonce 는 attestation 폴링과 destination mint 키로 저장한다    }}

백엔드Circle Attestation API 폴링 — exponential backoff

burn 직후 사용자 화면은 "처리 중"으로 두고 백엔드가 Circle API에서 attestation 을 받아온다. timeout은 길게(분 단위), backoff는 점진적으로.

CODE SURFACEtypescript
const ATTESTATION_URL = "https://iris-api.circle.com/v1/attestations";export async function pollAttestation(messageHash: `0x${string}`, opts: {  maxAttempts?: number;  initialDelayMs?: number;} = {}): Promise<`0x${string}` | "timeout"> {  const max = opts.maxAttempts ?? 60;  let delay = opts.initialDelayMs ?? 2000;  for (let i = 0; i < max; i++) {    const res = await fetch(`${ATTESTATION_URL}/${messageHash}`);    if (res.ok) {      const data = await res.json() as { status: "pending_confirmations" | "complete"; attestation: `0x${string}` };      if (data.status === "complete") return data.attestation;    }    await new Promise((r) => setTimeout(r, delay));    delay = Math.min(delay * 1.5, 30_000); // 최대 30초 간격  }  return "timeout";}

백엔드Destination chain에서 receiveMessage 실행

attestation을 받은 후 destination chain의 MessageTransmitter에 message + attestation 을 제출. 결과는 USDC mint event로 확인한다.

CODE SURFACEtypescript
import { writeContract, waitForTransactionReceipt } from "@wagmi/core";export async function finalizeOnDestination(args: {  destinationChainId: number;  message: `0x${string}`;  attestation: `0x${string}`;}) {  const txHash = await writeContract(config, {    chainId: args.destinationChainId,    address: MESSAGE_TRANSMITTER_ADDRESSES[args.destinationChainId],    abi: messageTransmitterAbi,    functionName: "receiveMessage",    args: [args.message, args.attestation]  });  const receipt = await waitForTransactionReceipt(config, { hash: txHash, chainId: args.destinationChainId });  // MintAndWithdraw event 가 있는지 확인 — 없으면 hooks 실패 가능성  const mintLog = receipt.logs.find((log) => log.topics[0] === MINT_AND_WITHDRAW_TOPIC);  if (!mintLog) {    throw new Error("destination mint event not found — check hooks execution");  }  return { txHash, mintLog };}

인덱서Cross-chain transfer 상태 추적 테이블

source burn / attestation / destination mint 가 다른 트랜잭션에서 일어나므로 하나의 행으로 통합 추적한다.

CODE SURFACEtypescript
// model CrossChainTransfer {//   id                  String   @id @default(cuid())//   userId              String//   amount              BigInt//   sourceChainId       Int//   destinationChainId  Int//   nonce               BigInt   // depositForBurn 반환값//   messageHash         String   @unique//   sourceTxHash        String//   sourceBurnedAt      DateTime//   attestation         String?  // Circle attestation hex//   attestedAt          DateTime?//   destinationTxHash   String?//   destinationMintedAt DateTime?//   status              String   // "burned" | "attested" | "minted" | "failed" | "timeout"////   @@index([status, sourceBurnedAt])// }

강의 포인트

표 자료가로 스크롤 · 크게 보기 지원
관점강의 중 확인할 질문학습 후 남길 증거
모델 구분CCTP가 wrapped token bridge와 다른 점은 무엇인가?burn/mint와 lock/mint 비교표
상태 저장source burn, attestation, destination mint를 모두 저장하는가?source tx, message hash, attestation, destination tx 필드
finality 정책Fast Transfer와 Standard Transfer 중 무엇을 쓰는가?SLA, fee, allowance, finality 정책표
사용자 문구attestation 지연을 사용자가 이해할 수 있는가?상태별 안내 문구
최신성CCTP V2 기준 설계와 V1 legacy 대응이 분리됐는가?확인일이 있는 migration 메모
크게 보기
관점강의 중 확인할 질문학습 후 남길 증거
모델 구분CCTP가 wrapped token bridge와 다른 점은 무엇인가?burn/mint와 lock/mint 비교표
상태 저장source burn, attestation, destination mint를 모두 저장하는가?source tx, message hash, attestation, destination tx 필드
finality 정책Fast Transfer와 Standard Transfer 중 무엇을 쓰는가?SLA, fee, allowance, finality 정책표
사용자 문구attestation 지연을 사용자가 이해할 수 있는가?상태별 안내 문구
최신성CCTP V2 기준 설계와 V1 legacy 대응이 분리됐는가?확인일이 있는 migration 메모

실무 예시

백엔드[CLIENT] [OPS] 사용자가 Base에서 USDC를 결제했지만 merchant settlement는 Ethereum에서 받아야 한다고 가정한다. 제품은 Base에서 burn을 시작하고, attestation을 받은 뒤 Ethereum에서 mint한다. 사용자는 "전송 중"만 보면 답답하지만, 아래처럼 상태를 나누면 support가 가능하다.

표 자료가로 스크롤 · 크게 보기 지원
상태사용자 문구운영자가 보는 데이터
BurnSubmittedsource chain에서 USDC 전송을 시작했습니다source tx hash, source domain
FinalityPendingsource chain 확인을 기다리고 있습니다finality threshold, block confirmations
AttestationPendingdestination mint 증명을 기다리고 있습니다Circle API status, message hash
MintSubmitteddestination chain에서 USDC 수령을 처리 중입니다destination tx hash
Minteddestination chain에서 USDC가 도착했습니다destination Transfer event
Delayed전송이 지연되어 확인 중입니다retry count, timeout, support note
크게 보기
상태사용자 문구운영자가 보는 데이터
BurnSubmittedsource chain에서 USDC 전송을 시작했습니다source tx hash, source domain
FinalityPendingsource chain 확인을 기다리고 있습니다finality threshold, block confirmations
AttestationPendingdestination mint 증명을 기다리고 있습니다Circle API status, message hash
MintSubmitteddestination chain에서 USDC 수령을 처리 중입니다destination tx hash
Minteddestination chain에서 USDC가 도착했습니다destination Transfer event
Delayed전송이 지연되어 확인 중입니다retry count, timeout, support note

이 예시는 checkout 상태머신과 연결된다. CCTP의 Minted가 되어야 merchant settlement로 넘어갈 수 있고, AttestationPending 상태에서는 사용자가 재결제를 누르지 않도록 해야 한다.

흔한 오해와 실패 시나리오

표 자료가로 스크롤 · 크게 보기 지원
오해실패 시나리오바로잡는 방법
CCTP도 일반 bridge와 같다고 설명한다사용자가 wrapped token risk와 native burn/mint 모델을 혼동한다UI와 문서에서 CCTP burn/mint를 분리해 설명한다
burn이 성공하면 destination 결제도 완료라고 본다attestation 또는 mint 지연 중 merchant settlement를 진행한다burn, attestation, mint 상태를 분리한다
Fast Transfer는 항상 빠르다고 가정한다allowance 부족이나 fee 정책 변경으로 UX가 깨진다allowance와 fee 조회 실패 상태를 둔다
supported chain 목록을 코드에 고정한다Circle 지원 범위나 domain 정보 변경을 놓친다공식 문서 URL과 마지막 확인일을 allowlist에 저장한다
hooks 실행 성공과 USDC mint 성공을 같은 것으로 본다destination logic 실패가 결제 실패로 잘못 처리된다mint 상태와 hook execution 상태를 분리한다
크게 보기
오해실패 시나리오바로잡는 방법
CCTP도 일반 bridge와 같다고 설명한다사용자가 wrapped token risk와 native burn/mint 모델을 혼동한다UI와 문서에서 CCTP burn/mint를 분리해 설명한다
burn이 성공하면 destination 결제도 완료라고 본다attestation 또는 mint 지연 중 merchant settlement를 진행한다burn, attestation, mint 상태를 분리한다
Fast Transfer는 항상 빠르다고 가정한다allowance 부족이나 fee 정책 변경으로 UX가 깨진다allowance와 fee 조회 실패 상태를 둔다
supported chain 목록을 코드에 고정한다Circle 지원 범위나 domain 정보 변경을 놓친다공식 문서 URL과 마지막 확인일을 allowlist에 저장한다
hooks 실행 성공과 USDC mint 성공을 같은 것으로 본다destination logic 실패가 결제 실패로 잘못 처리된다mint 상태와 hook execution 상태를 분리한다

실습 과제

  1. 백엔드[INDEXER] CCTP 상태와 증거 정리하기: source burn, finality, attestation, destination mint, fee, allowance 상태마다 저장할 tx hash, message hash, domain ID, API response를 정리한다.
  2. 백엔드Fast/Standard Transfer 정책 작성하기: Fast Transfer와 Standard Transfer 중 어떤 상황에서 어느 방식을 쓸지 SLA, fee, allowance, finality, support message 기준으로 정책표를 만든다.
  3. 운영CCTP V2 전환 메모 작성하기: 새 설계를 CCTP V2 기준으로 둔다는 전제, V1 legacy phase-out 확인일, 지원 체인/domain 재확인 절차를 설계 메모로 작성한다.

완료 기준

  1. burn/mint 모델을 설명한다.
  2. attestation 대기 UX를 설계했다.
  3. 체인별 리스크 메모를 작성했다.
  4. CCTP V1 legacy phase-out과 V2 기준 설계 메모를 남겼다.

근거 자료

Final checkpoint

읽기를 마쳤다면 여기서 기록한다

아래 버튼은 읽기 진도를 저장한다. 체크리스트, 과제, 랩 산출물은 위 Workbook에서 따로 관리한다.

  • burn/mint 모델을 설명한다.
  • attestation 대기 UX를 설계했다.
  • 체인별 리스크 메모를 작성했다.

학습 자료 근거

CCTP USDC 크로스체인
이 LMS 레슨의 개념, 예시, 과제 구성을 잡는 데 사용한 근거 문서.
내부 참고 문서
Circle CCTP Documentation
https://developers.circle.com/cctp
Circle: Migrate from CCTP V1 to V2
https://developers.circle.com/cctp/migration-from-v1-to-v2
Circle: Fast Transfer Allowance
https://developers.circle.com/cctp/concepts/fast-transfer-allowance
Circle: CCTP Fees
https://developers.circle.com/cctp/concepts/fees