암호화폐 사용자들 사이에는 널리 퍼진 믿음이 있다: 주소를 정확히 입력하고, 올바른 네트워크를 선택하며, 수수료를 감당할 만큼 네이티브 토큰이 충분하면 거래는 성공할 것이다. 대다수의 경우 그 믿음은 맞다.
하지만 각 블록체인에는 고유한 숨은 규칙들이 — 매우 특정한 상황에서만 드러나는 — 존재한다. 그런 규칙이 작동하면, 외견상 모든 것을 올바르게 했음에도 거래가 실패할 수 있다.
바로 이런 일이 우리에게도 일어났다. 우리 스왑 서비스인 Rabbit.io의 고객이 LTC를 SOL로 스왑할 때였다. 금액은 아주 작았다 — 0.0003 SOL 미만, 현 시세로 3센트도 채 안 되는 액수였다. 이전 글들에서 말했듯이 우리는 스왑에 상한을 두지 않는다. 매우 작은 금액도 자주 처리한다.
요청받은 SOL을 보내려 할 때 문제가 발생했다. 주소는 정확했다. 네트워크는 Solana Mainnet이었다. 잔고도 충분했고, 수수료도 계산에 반영되어 있었다. 그런데도 거래는 계속 실패했다. 반복해서.
설명은 예상 밖이었다 — 우리에게도 마찬가지였다. 고객에게 설명했을 때 그는 처음에 믿지 않았다. 이 사건을 계기로 나는 의문을 품었다: 다른 블록체인들에도 이와 비슷한 숨은 함정이 얼마나 많을까?
그래서 나는 조사에 나서 여러 흥미로운 사례를 모으기로 했다 — 매일 다양한 암호화폐 교환을 처리하는 나조차도 방심하면 걸릴 수 있는 사례들이다.
많은 사람은 Solana에서 USDT, USDC 등 SPL 토큰을 이전한 적이 없는 주소로 받으려면 그 주소에 소량의 SOL이 있어야 한다는 말을 들어본 적이 있다. 엄밀히 말해 그것이 완전히 정확한 설명은 아니지만, 일정 부분 사실이다.
각 토큰은 별도의 Associated Token Account(ATA)에 저장되며, 그 계정을 생성하는 데는 대략 0.002 SOL 정도가 든다. 하지만 수취인의 잔고가 0이어도 토큰을 받을 수는 있다 — ATA 생성 비용은 수취인이 아니라 송금인이 부담하기 때문이다.
덜 알려진 사실은 유사한 요구가 때때로 네이티브 SOL 전송에도 적용될 수 있다는 점이다. 우리 고객이 요청한 아주 작은 금액을 보낼 때 바로 그런 일이 발생했다.
Solana에서 계정은 단순한 주소 문자열이 아니다. 블록체인의 현재 상태에 저장된 실제 레코드다. 그 레코드는 검증자 노드들의 디스크 공간을 차지하며 — 저장 공간은 무료가 아니다.
이 메커니즘은 렌트 면제(rent exemption)로 알려져 있다. 계정이 존재하려면 잔고가 일정 최소치를 초과해야 하는데, 실질적으로는 약 2년치 저장 임대료에 해당하는 비용이다.
이 값이 영구적으로 고정된 것은 아니지만, 현재 기본 시스템 계정(일반 지갑)의 최소치는 890,880 라파트(lamports), 즉 0.00089088 SOL이다.
주소가 아직 온체인에 존재하지 않는 경우(즉 잔고가 0인 경우), 이 임계값보다 적은 금액으로 계정 생성 시도를 하는 최초 거래는 거부된다.
바로 이것이 우리 사례에서 일어나고 있던 일이다. 우리는 완전히 새 주소로 약 0.0003 SOL보다 조금 적은 금액을 보내려 했고 — 요구되는 최소치의 대략 3분의 1 수준이었다. 우리 고객은 그런 규칙이 존재한다는 것을 믿지 않았다. 솔직히 말해, Solana 공식 문서에서 이를 분명히 언급한 문구를 찾는 것도 쉽지 않았다. 간접적인 확인은 RPC 메서드 getMinimumBalanceForRentExemption의 설명에서 찾을 수 있었지만, 명시적으로 이 규칙을 적어놓지는 않았다.
여기에는 중요한 뉘앙스가 있다. 우리는 이전에도 매우 작은 금액들을 고객들에게 여러 번 보낸 적이 있고 문제를 겪지 않았다. 실무적으로 Solana는 임의로 작은 액수의 전송을 허용한다는 것을 알고 있었다.
그리고 그것은 사실이다 — 단 수취 계정이 이미 존재하는 경우에만 해당한다. 두 번째, 세 번째 또는 백 번째로 들어오는 거래라면 주소는 1 라파트도 받을 수 있다. 하지만 계정이 처음 생성되는 상황에서는 렌트 면제 체크가 적용된다.
Solana에서 겪은 문제는 사실 더 넓은 패턴의 한 사례다. 여러 네트워크에서는 암호학적 객체로서의 주소와 분산 원장에 기록된 엔트리로서의 계정이 서로 다른 개념이다. 초기 최소 잔고가 없으면 그 계정은 단순히 존재하지 않는다.
XRP Ledger에서는 모든 신규 계정이 최소 1 XRP의 예치금을 받아야 한다. 네트워크 초창기에는 이 요구치가 훨씬 높았는데 — 런칭 시점에는 200 XRP, 이후 50 XRP, 20 XRP, 10 XRP로 점차 낮아졌다. 시간이 지나며 XRP 가격이 오르자 예치금은 현재의 1 XRP로 점차 조정되었다.
이 예치금은 계정에 영구적으로 잠겨 있다. 지출할 수 없고, 거래 수수료로도 사용할 수 없다. 일부 XRP Ledger 탐색기들은 총 잔고와 잠긴 예치금을 별도로 표시해 지갑 소유자가 실제로 지출 가능한 XRP가 얼마인지 명확히 볼 수 있게 한다.
Stellar 네트워크도 유사한 논리를 따른다. 원장에 계정으로 기록되려면 최소 잔고가 필요하다. Stellar 계정은 활성화되고 운영되려면 최소 1 XLM이 필요하다.
내가 한때 1,000 USDC가 1 XLM으로 변한 실수에 대해 썼던 것을 기억할 것이다. 그 글에서 설명한 상황은 바로 이 메커니즘의 전형적인 사례였다: 토큰 거래는 송신자가 올바르게 서명했더라도 특정 전제 조건이 충족되지 않으면 수취인에게 입금될 수 없다.
XRP Ledger와 Stellar의 특징 중 하나는 계정이 특정 자산과 상호작용하기 위해 명시적으로 옵트인(opt-in)하지 않으면 그 자산을 받을 수 없다는 것이다. 이는 개발자들이 의도적으로 설계한 결정이며, 장점이 있다.
다른 네트워크에서는 고래나 유명인 주소가 새로 생성된 스팸 토큰으로 넘쳐나, 마치 주소 소유자가 그 토큰을 샀다는 착시를 주는 일이 흔하다. 그러나 XRP Ledger와 Stellar에서는 자산 수신에 사전 권한이 필요하기 때문에 이런 스팸은 불가능하다.
라이트닝 네트워크는 비트코인을 블록체인에 모든 전송을 기록하지 않고 보내기 위해 설계된 결제 채널들의 네트워크다. 일반적인 온체인 거래와 달리 라이트닝 결제는 네트워크의 모든 노드에 의해 검증되거나 채굴자에 의해 블록에 확정되지 않는다. 대신 중간 노드들의 체인을 통해 전달된다.
바로 이 지점에서 비직관적인 오류들이 발생한다.
페이먼트가 Alice에서 Bob과 Carol을 거쳐 David에게 도달하려면, 경로의 각 구간이 요구되는 방향으로 충분한 유동성을 갖추고 있어야 한다.
네트워크는 각 채널의 총 용량은 알지만, 그 잔액이 양 당사자 사이에 어떻게 분배되어 있는지는 모른다. 이로 인해 다음과 같은 상황이 생길 수 있다:

결과적으로 Alice에서 David로의 결제는 "route not found" 오류로 실패한다 — Alice는 충분한 자금을 가지고 있고, David는 결제를 받을 수 있는 활성 채널을 가지고 있으며, 네트워크 그래프상으로는 경로가 형식적으로 존재함에도 불구하고.
또 하나 특히 까다로운 뉘앙스가 있다. 가장 널리 사용되는 라이트닝 노드 구현체 중 하나인 LND에서는 기본 라우팅 수수료 한도가 0 사토시로 설정되어 있다.
이는 노드가 완전히 무료인 경로만 찾으려 시도한다는 뜻인데, 실제 네트워크에서는 그런 경로는 거의 존재하지 않는다. 기본 설정을 변경하지 않은 사용자들은 작은 라우팅 수수료를 허용하면 결제가 성공할 수 있음에도 불구하고 반복적으로 "route not found" 오류를 볼 수 있다.
이것은 버그가 아니다. 오히려 LND 개발자들은 사용자를 대신해 "최대 얼마의 수수료를 기꺼이 낼 것인가"를 결정하지 않기로 의도적으로 선택한 것이다. 만약 기본값으로 양수 한도가 설정되어 있었다면, 사용자는 나중에 자신도 모르는 사이에 라우팅 수수료가 차감되었다는 사실을 알게 될 수 있다.
"route not found" 오류의 다른 원인들:
우리는 라이트닝 네트워크를 통해 비트코인을 교환할 때 이 모든 문제를 겪는다. 전통적인 블록체인 거래와 비교하면 이 문제들은 특이하게 보일 수 있지만, 각 문제에는 해결 방법이 있다. 그래서 Rabbit.io에서는 이러한 함정을 걱정할 필요 없이 라이트닝을 통해 비트코인을 송수신할 수 있다.
USDT와 USDC는 중앙에서 관리되는 스테이블코인이다. 그들의 스마트 계약에는 블랙리스트 기능이 포함되어 있어 발행사인 Tether와 Circle은 언제든지 주소를 블랙리스트에 추가할 수 있고, 그 이후 해당 주소에서의 토큰 관련 작업은 불가능해진다.
이것은 잘 알려진 사실이다. 덜 알려진 점은 USDT와 USDC의 제한 메커니즘이 근본적으로 다르며, 그 차이가 실무적으로 중요한 결과를 초래한다는 것이다.
USDC 계약에서는 블랙리스트 체크가 송신자와 수신자 모두에 적용된다. 수신자 주소가 블랙리스트에 올라 있다면 스마트 계약이 거래를 거부한다. 송신자는 소량의 가스비를 잃을 뿐이며, USDC는 그의 지갑에 남아 있다.
송신자 관점에서는 자금이 얼어붙은 주소로 사라지지 않기 때문에 이 모델이 더 안전하다고 볼 수 있다.
USDT 계약(TetherToken)에서는 transfer 함수에 다음과 같은 체크가 들어 있다: require(!isBlackListed[msg.sender]). 이는 블랙리스트 검사 대상이 오직 송신자뿐이라는 뜻이다. 전송 과정에서 수신자는 검증되지 않는다.
실무적으로 이는 다음과 같은 결과를 낳는다:
수취인이나 다른 누구도 이후에 그 토큰을 옮길 수 없다 — 블랙리스트된 주소에서의 출금 거래는 금지되어 있다. 발행사인 Tether만이 destroyBlackFunds 함수를 사용해 블랙리스트된 주소의 토큰을 실질적으로 소멸시킬 수 있다.
이 경우에는 올바르게 구성된 거래가 처음부터 실패하는 편이 낫다고 주장할 수도 있다. USDC 계약에서 구현된 접근법이, 거래는 성공하지만 결국 전송이 무의미해지는 USDT 모델보다 투명하고 공정해 보인다.
내 권장 사항:
그렇다고 해서 모든 USDT 전송이 무조건 성공하는 것은 아니다 — 모든 거래 데이터가 올바르게 보일 때조차도 가끔은 전송이 아예 실패할 수 있다. 이 글의 파트 II에서는 그러한 사례들과 비트코인, 이더리움, 트론 네트워크의 덜 알려진 거래 제약들을 다룰 예정이다.
파트 II는 정확히 일주일 후 이곳에 게시될 것이다.