시냅스

Road to Web3 (9) 토큰 vs 네이티브 코인: USDC는 있는데 왜 전송이 안 되죠 본문

Road To Web3/Blockchain

Road to Web3 (9) 토큰 vs 네이티브 코인: USDC는 있는데 왜 전송이 안 되죠

ted k 2025. 12. 29. 20:25
이 글은 Ethereum 및 EVM 계열을 기준으로 설명합니다.

 

이번 편의 목표는 딱 하나입니다.

  • 잔액이 있는데도 전송이 실패하는 이유를 수수료 모델과 계정 모델로 설명하기

 

제가 처음 토큰 전송을 해보려다 막혔던 상황이 딱 이거였습니다.

  • 지갑에 USDC는 보인다
  • 그런데 전송을 누르면 실패하거나 pending 이 오래 간다
  • 또는 approve 라는 단계를 요구한다

원인은 거의 항상 하나로 수렴합니다.

  • 수수료는 네이티브 코인으로 낸다

그리고 토큰 전송은 사실 단순 송금이 아니라 컨트랙트 호출입니다.


 

요약

구분 네이티브 코인 ETH 토큰, ERC20(e.g. USDC)
존재 형태 프로토콜 기본 자산 컨트랙트가 관리하는 잔고
수수료 지불 본인 자체로 지불 네이티브 코인 필요
전송 방식 프로토콜 레벨 잔액 이동 컨트랙트 함수 호출 transfer
실패 원인 수수료 부족, nonce, 혼잡 수수료 부족 + allowance + 컨트랙트 revert
조회 balanceOf 개념이 프로토콜에 내장 balanceOf 컨트랙트 호출

 

1) 네이티브 코인과 토큰은 계층이 다르다

네이티브 코인은 체인의 1층 자산입니다.

  • 계정 잔액은 프로토콜 상태에 직접 존재
  • 전송은 프로토콜 레벨 상태 변경

반면 토큰은 2층 앱 자산입니다.

  • 컨트랙트가 내부 매핑으로 잔액을 관리
  • 전송은 컨트랙트 코드 실행 결과

이 차이가 모든 UX 문제의 출발점입니다.

 


 

2) USDC 전송은 단순 송금이 아니라 컨트랙트 호출이다

USDC를 전송한다는 말은 이런 행위입니다.

  • to 는 USDC 컨트랙트 주소
  • data 는 transfer(recipient, amount) 호출 인코딩
  • value 는 보통 0

즉, ETH 전송은 잔액 이동이고
USDC 전송은 컨트랙트 함수 호출입니다.

그래서 실패 원인도 컨트랙트 로직 범주까지 확장됩니다.


 

3) USDC는 있는데 왜 전송이 안 되나

가장 많은 원인 7가지를 정리하면 이렇습니다.

1. 수수료로 낼 네이티브 코인이 없다

USDC 전송 트랜잭션도 가스를 씁니다.
그 가스비는 ETH 같은 네이티브 코인으로 냅니다.

  • 지갑에 USDC 100 있어도
  • ETH 0 이면
  • USDC 전송 트랜잭션 자체를 실행할 수 없습니다

이게 가장 흔한 원인입니다.

2. 네트워크가 다르다, 체인이 다르다

USDC는 체인마다 다른 컨트랙트 주소입니다.

  • Ethereum mainnet 의 USDC
  • Base 의 USDC
  • Arbitrum 의 USDC

이들은 이름은 같아도 다른 자산처럼 동작합니다.
지갑에서 보이는 USDC가 어느 네트워크에 있는지부터 확인해야 합니다.

3. 토큰 컨트랙트 정책으로 revert

토큰은 컨트랙트라서 정책이 있습니다.

  • 블랙리스트
  • 일시 중지 pause
  • 전송 제한

이런 정책에 걸리면 트랜잭션은 revert 로 실패할 수 있습니다.

4. allowance 승인 단계가 필요하다

USDC를 상대 서비스가 내 잔고에서 가져가게 하려면 approve 가 필요합니다.

  • 내가 직접 transfer 하는 건 approve 불필요
  • 서비스가 transferFrom 으로 가져가려면 approve 필요

즉, 결제형 UX 에서 approve 가 튀어나오는 겁니다.

5. nonce 문제로 pending 또는 replaced

계정의 nonce가 어긋나면 전송이 늦어질 수 있습니다.

  • 낮은 수수료로 보낸 tx가 pending 으로 막혀있으면
  • 그 뒤 nonce tx는 같이 줄이 막힌다

그래서 pending 정리가 필요할 때가 있습니다.

6. 수수료 설정이 낮아서 오래 대기

baseFee 와 tip 설정이 낮으면 블록에 못 들어갑니다.
지갑은 보통 자동으로 조정하지만, 수동 설정은 실수하기 쉽습니다.

7. 수신자 주소 실수 또는 잘못된 체인

토큰은 주소가 맞더라도, 상대가 그 체인을 지원하지 않으면 사실상 분실처럼 됩니다.
예: 거래소 입금 주소에 다른 체인에서 전송

블록체인에서 취소와 회수는 기본값이 아닙니다.


 

4) 결제 시스템 비유로 다시 보기

USDC는 있는데 왜 전송이 안 되냐를 web2 결제 언어로 바꾸면 이렇습니다.

  • USDC 잔액은 상품권 잔액
  • 수수료 ETH 는 택배비
  • transfer 는 내가 직접 보내는 택배 접수
  • transferFrom 결제는 가맹점이 내 상품권 잔액에서 차감하는 모델
  • approve 는 자동이체 권한 등록

상품권이 있어도 택배비가 없으면 발송을 못 하듯,
토큰이 있어도 수수료용 네이티브 코인이 없으면 전송이 안 됩니다.


 

5) 실패 시나리오별 체크리스트

증상 흔한 원인 확인 순서
즉시 실패 ETH 수수료 부족, revert ETH 잔액, 시뮬레이션 에러
pending 오래 수수료 낮음, nonce 줄막힘 가스 제안값, pending tx
replaced 같은 nonce로 더 높은 수수료 tx 지갑 히스토리
보냈는데 상대가 못 받음 체인 불일치, 주소 네트워크 불일치 네트워크, 입금 안내

 

6) 체크리스트

1) 전송 자산과 수수료 자산이 다르다는 문장을 UI 에 박아둔다
2) 네트워크 선택을 사용자가 틀리기 어렵게 만든다
3) 토큰 전송은 컨트랙트 호출이라 revert 이유를 노출할 수 있으면 노출한다
4) 결제형이면 approve 와 transferFrom 모델을 명확히 구분한다
5) allowance 는 최소 권한 원칙으로 설정하고 무한 승인 UX 를 경계한다
6) pending 상태 머신을 구현하고 timeout 과 재시도 정책을 둔다
7) 입금 주소는 체인까지 포함해 검증한다
8) 최소 수수료 잔액 부족 시 안내와 onramp 동선을 제공한다
9) 운영자는 receipt 와 event 를 기반으로 상태를 추적한다


 

결론

  • 토큰은 컨트랙트가 관리하는 2층 자산이고, 전송은 컨트랙트 호출이다
  • 가스비는 네이티브 코인으로 내기 때문에, 토큰만 있으면 전송이 막힌다
  • approve 와 transferFrom 은 결제형 UX 에서 자연스럽게 등장한다
  • 체인 불일치는 실무에서 가장 비싼 실수다
Comments