
한 단어가 불러온 '사건': Akutar NFT, 2억 달러 자금 영구 잠금
본문의 저자인 천건 제이슨은 위챗 공식 계정 '오늘 더 이 세계를 이해하고 있는가?'에서 발행되었습니다. 독자 여러분께서는 해당 공식 계정을 팔로우하시면 과거 기사들을 확인하실 수 있으며, 저자와 더욱 깊이 있게 소통할 수도 있습니다. 또한 트위터 @jason_chen998를 팔로우하시면 최신 소식을 받아보실 수 있습니다.
지난번 NBA 관련 글을 작성한 후 너무 피로해서 잠시 쉬고 싶었지만, web3 세계는 너무나 흥미진진하여 매일 대형 뉴스들이 쏟아지고 있어 주말에도 어쩔 수 없이 다시 일을 시작하게 되었습니다.

오늘 Akutar라는 NFT 프로젝트가 스마트 계약 버그로 인해 11,539개의 ETH(약 3,400만 달러, 한화 약 2억 원)에 달하는 자금이 영원히 인출 불가 상태가 되어 봉쇄되었습니다. 무려 2억 원입니다!
계약 주소를 열어 이 2억 원을 한번 구경해보고, Akutar 팀이 이 숫자를 바라보며 통곡하는 모습을 상상해봅시다.

먼저 Akutar에 대해 소개하자면, 공식 홈페이지 설명과 트위터를 통해 알 수 있듯이 이 프로젝트는 허접한 토속 프로젝트가 아니라 정성껏 준비된 고품질 프로젝트입니다. 세심한 일러스트 스타일이나 로드맵의 완성도 모두 매우 높습니다.

이 프로젝트의 창시자는 유명한 야구 선수 미차 존슨(Micha Johnson)으로, 우연히 한 흑인 소년이 어머니에게 "흑인도 우주비행사가 될 수 있나요?"라고 묻는 대화를 듣고 감동받아, 헬멧을 쓴 흑인 소년이 우주비행사를 꿈꾸는 시리즈 NFT를 발행하기로 결심했습니다. 다소 아름다운 이야기죠.

그렇다면 이렇게 따뜻한 이야기 뒤에 숨겨진 NFT가 왜 망했을까요? 어느 정도는 프로젝트팀이 따뜻한 사회공헌보다 수익 추구에 더 집착하다 보니 스스로 발등을 찍은 결과입니다. 그들은 비교적 특별한 방식인 네덜란드 경매(Dutch Auction)를 사용했습니다.
전통적인 경매 방식은 낮은 가격을 설정한 후 사람들이 점점 더 높은 가격을 제시하며, 가장 높은 입찰자가 물건을 얻는 영국식 경매입니다. 반면 네덜란드식 경매는 처음에 높은 가격을 설정한 후 점차 낮춰가며, 누군가 특정 가격에서 구매 결정을 내리는 방식입니다. 네덜란드식 경매는 인간 심리를 시험하는데, 모두가 가장 낮은 가격을 기다리지만 동시에 다른 사람이 먼저 사버릴까 두려워하죠.
Azuki도 네덜란드 경매 방식을 사용하지만, Akutar은 Azuki보다 한 걸음 더 나아갔습니다. Azuki는 가격이 동적으로 하락하여 늦게 살수록 가격이 낮아지고, 일찍 살 경우 비싸게 산 셈이 됩니다. Akutar은 여기에 ‘환불’ 규칙을 추가했는데, 일견 사용자 친화적으로 보이지만 사실은 더 많은 수익을 얻기 위한 전략이라고 생각합니다.
아래 이미지처럼 경매 시작 가격은 3.5ETH이며, 6분마다 1ETH씩 하락합니다. 최종적으로 가장 낮은 가격에 구매한 사람이 기준이 되며, 그보다 높은 가격에 산 모든 사용자에게 차액을 환불해줍니다. 예를 들어 최저 판매 가격이 1.5ETH라면, 1.5ETH 이상을 지불한 모든 사용자에게 차액이 돌려갑니다. 이 방식은 사용자들이 최저가를 노리지 않고 자신 있게 구매하도록 유도하는 것입니다. 비싸게 샀더라도 환불이 되니까요.

따라서 Akutar에는 거대한 자금 풀이 존재하여 모든 사용자가 낸 돈을 저장합니다. 이 자금은 프로젝트팀 몫과 환불해야 할 사용자 몫을 포함합니다. 여기서 한 가지 지식을 소개하자면, 이전 글에서도 언급했듯이 스마트 계약도 개인 지갑 주소와 마찬가지로 하나의 블록체인 주소이며, 암호화폐를 받고 보낼 수 있습니다. 여러분이 어떤 프로젝트의 NFT를 민팅(mint)할 때 실제로는 먼저 돈을 스마트 계약 주소로 보내고, 계약이 NFT를 전달하는 것입니다. 즉, 모든 NFT 1차 시장 판매에서 돈은 먼저 계약 주소로 들어간 후, 프로젝트팀이 인출 작업을 통해 자신의 지갑으로 옮기는 것입니다.
이번에 2억 원이 봉쇄된 것도 바로 이 인출 과정에서 버그가 발생했기 때문입니다. 블록체인 스마트 계약은 수정 불가능하기 때문에 버그가 생기면 고칠 수 없습니다. 전통 인터넷 서비스에서 문제가 생기면 패치하면 되지만, web3에서는 이 2억 원을 평생 바라보기만 할 수 있다는 의미입니다.
몇 가지 핵심 코드를 통해 원리를 이해하고, 어디서 문제가 발생했는지 분석해보겠습니다.
먼저 네덜란드 경매의 원리를 살펴보겠습니다. 현재 가격을 가져오는 함수부터 보면, 우선 최신 블록의 시간인 block.timestamp을 가져옵니다. 이후 현재 시간에서 시작 시간 startAt을 빼고 6으로 나눕니다(6분마다 가격이 하락하므로). 이를 통해 얼마나 자주 가격이 하락했는지(timeElapsed)를 계산하고, 이를 하락 금액과 곱해 전체 할인 금액(discount)을 산정합니다. 마지막으로 시작 가격 startingPrice에서 할인 금액을 빼 현재 지불해야 할 금액을 결정합니다. 참고로 코드에서 언급된 금융 관련 파라미터들은 미리 계약에 고정된 것이 아니라 변경 가능한 변수입니다. 즉 프로젝트팀이 상황에 따라 금액을 조정할 수 있도록 백도어를 남긴 것입니다.

가격 산정 방식을 이해했으니, 이제 사용자의 입찰 과정에서 무슨 일이 일어나는지 살펴보겠습니다. 코드가 너무 길어서 전부 붙이진 않겠고 핵심 부분만 설명하겠습니다.
먼저 위에서 언급한 현재 가격을 가져온 다음, 사용자가 구매하고자 하는 수량(amount)을 곱해 총 지불 금액(totalPrice)을 계산합니다. 이후 사용자가 실제로 보낸 값(value)이 총 금액보다 큰지 확인하고, 크다면 아래 로직을 실행합니다.

여기서 입찰(bid)이 무엇을 포함하는지 정의합니다. 입찰자 주소(bidder), 입찰 가격(price), 총 구매 수량(bidsPlaced), 환불 상태(finalProcess, 0: 미환불, 1: 환불완료, 2: 환불취소).

여기서 첫 번째 함정이 등장합니다: totalBids는 지금까지 판매된 NFT의 총 수량을 나타내며 기본값은 0입니다. 사용자가 입찰할 때마다 구매 수량(amount)을 더합니다. 이 값을 기억하세요. 나중에 중요합니다.

두 번째 함정: bidIndex라는 파라미터를 사용해 입찰한 사용자 수를 저장합니다. 이 두 파라미터를 기억하세요. totalBids는 총 판매된 NFT 수량, bidIndex는 입찰한 사람 수입니다.

다음으로 프로젝트팀이 사용자에게 환불하는 과정을 설명합니다. 프로젝트팀은 processRefunds라는 버튼을 클릭해 환불을 시작합니다. 이 버튼의 로직은 모든 입찰자들을 순회하며 처리하는 것으로, 반복 횟수는 앞서 말한 bidIndex(입찰자 수)입니다. 처리 내용은 먼저 해당 사용자의 finalProcess 상태가 0인지 확인하고(0은 아직 환불되지 않음을 의미), 0이면 계속 진행됩니다. 당시 입찰 가격에서 최저 거래 가격을 빼고 구매 수량을 곱하면 환불 금액(refund)이 됩니다.
그 후 해당 사용자의 finalProcess 상태를 0에서 1로 변경하여 환불 완료 상태로 표시하고, 중복 환불을 방지합니다.
refundProgress는 환불 완료된 인원 수를 기록하는 파라미터로, 한 명을 환불할 때마다 1씩 증가합니다. bidIndex만큼 반복하므로 refundProgress와 bidIndex는 일치해야 합니다. 원론적으로 문제는 없지만! 아래를 보세요!

프로젝트팀의 인출 로직은 어떻게 구성되어 있으며, 어떤 결함으로 인해 인출이 불가능해졌을까요?
아래는 프로젝트팀이 자금을 인출할 수 있는 함수입니다. 즉 claimProjectFunds 버튼을 누르면 자신의 지갑으로 돈을 옮길 수 있는데, 세 단계의 검증을 거칩니다. 첫 번째는 경매가 종료되었는지 확인하고, 종료되면 두 번째 검증인 환불 완료 인원(refundProgress)이 입찰자 수(bidIndex)보다 많은지 확인합니다. 사실 프로젝트팀의 의도는 좋은 것입니다. 모든 사용자에게 환불이 완료된 후에야 인출을 허용하려는 것입니다. 하지만 여기서 문제가 발생했습니다. totalBids가 무엇인지 기억하십니까? 그것은 판매된 NFT 수량이지 입찰자 수가 아닙니다!

왜 이것이 문제냐고요? 한 사용자가 여러 개의 NFT를 구매할 수 있으므로, 환불 인원은 실제 구매 인원입니다. 그런데 구매 인원 수가 판매된 NFT 수량보다 많아야 한다고 요구하고 있습니다. 그러나 한 사람이 여러 개를 살 수 있으므로, 단 한 명이라도 2개를 사면 구매 인원 수가 절대 NFT 수량을 넘을 수 없습니다. 예를 들어 10명이 11개를 샀다면, 어떻게 10이 11보다 클 수 있겠습니까?
이더스캔(Etherscan)에서 확인해보면, refundProgress는 3,699입니다. 즉 입찰한 사람은 총 3,699명입니다. 하지만 totalBids는 5,495로, 총 5,495개의 NFT가 판매된 것입니다. 3,699보다 훨씬 많습니다. 따라서 refundProgress가 totalBids를 넘는 일은 영원히 불가능하며, 이 2억 원은 앞으로도 영원히 계약 안에 봉쇄된 채 후세 사람들의 견학 대상이 될 것입니다.


즉 프로젝트팀이 단어를 잘못 쓴 것입니다. 원래 bidIndex(입찰자 수)를 써야 했는데, 실수로 totalBids(판매 수량)를 적었습니다. 단 한 단어로 2억 원이 사라졌습니다. 아마도 세계에서 가장 비싼 단어일 것입니다. 여러분 모두 이 단어 'totalBids'를 반드시 기억하세요. 이 단어 하나가 무려 2억 원의 가치를 지녔습니다!
이 글을 통해 여러분과 함께 새로운 민팅 방식인 네덜란드 경매의 원리를 배워보았으며, 또한 'totalBids'라는 2억 원짜리 단어도 함께 알아보았습니다.
TechFlow 공식 커뮤니티에 오신 것을 환영합니다
Telegram 구독 그룹:https://t.me/TechFlowDaily
트위터 공식 계정:https://x.com/TechFlowPost
트위터 영어 계정:https://x.com/BlockFlow_News














