
一個單詞引發的“血案”:Akutar NFT 2億資金被永久鎖死
TechFlow Selected深潮精選

一個單詞引發的“血案”:Akutar NFT 2億資金被永久鎖死
通過這篇文章帶著大家學習了一種新的mint方式荷蘭拍以及其原理,另外帶大家認識了一個2個億的單詞totalBids。
本文作者陳劍jason,來自微信公眾號“今天有更懂這個世界一點了嗎”,大家可以關注公眾號查閱往期文章與作者進行更深入溝通,也可以關注作者twitter: jason_chen998獲取最新動態。
上篇寫NBA的文章寫的太累了大傷元氣,想休息一段時間再寫的,結果web3的世界實在是太精彩,每天發生的大新聞太多,大週末的又被迫營業。

今天一個叫Akutar的NFT項目因為合約bug,導致11539個ETH,價值3400萬美金2個億人民幣的錢永久取不出來被鎖死了,2個億啊!
我們打開合約地址看看這2個億來眼饞眼饞,想象一下Akutar團隊望著這一串數字的抱頭痛哭的心情。

首先介紹一下Akutar,從官網的描述和他們twitter可以看出,這不是一個土狗項目,相反是一個很用心的高質量項目,不論是從精細的畫風還是roadmap描述質量都很高。

它的發起人是一位知名的棒球運動員Micha Johnson,起源於他無意間聽到一位黑人小男孩與母親的對話,小男孩問母親宇航員能否是黑人,所以Micha Johnson決定發行一系列夢想成為宇航員的戴著頭盔的黑人小男孩,一個還算美妙的故事。

那麼看著這麼暖心的故事背後的NFT這麼就砸了呢?從某種程度上還是項目方對於賺錢的渴望大過於所謂的暖心公益,從而搬起石頭砸了自己的腳,因為它使用了一種比較獨特的荷蘭拍方式。
傳統的拍賣方式是設置一個低價,然後大家向上叫價,最終出價最高者可購買,這是英式拍賣;荷蘭式拍賣則是先設置一個最高價,然後逐漸的降價,最終有人在某個價格點出手將其買下來,荷蘭拍更考驗人性,因為每個人都想等最低價,但是都怕別人先於自己購買。
Azuki就是用的是荷蘭拍,但是Akutar相比於Azuki的拍賣方式又做了改變,Azuki的價格是動態下降的,從而買的越晚價格越低,買的越早可能就吃了虧價格會高。Akutar則加了一個“退款”規則,該規則看起來好像對用戶更友好但是我認為實際上是想割更多的錢。
這如下圖所示,拍賣起始價格是3.5ETH,每過6分鐘降低1ETH,最終最低價格購買的人將成為標準價格,其他高於該價格購買的用戶將獲得退款,比如最後最低出售價格是1.5ETH,那所有高於1.5ETH出價的人均會獲得差價的退款,這種實際上是想讓用戶放心大膽的去買,不要蹲守最低價,即使買高了也能退款。

所以Akutar會有一個巨大的資金池用於存儲所有用戶交的錢,這部分錢包括項目方自己應得的,也包括需要退給用戶的。這裡先科普一個知識,之前的文章中也提到過,智能合約的性質和你自己個人的錢包地址是一樣的,都是一個區塊鏈地址,可以接收、發送虛擬貨幣,當你在mint某個項目時,實際上是你先將錢打到項目合約地址,然後合約給你轉一個NFT,即所有NFT的一級市場發售,錢都是先到了合約地址,再由項目方去進行提款操作,將合約裡面的錢提到自己的錢包中。
這次2個億被鎖死就是因為在提款這個步驟出了bug,因為區塊鏈智能合約不可篡改的特性使得出現了bug是沒法修的,傳統互聯網如果有個bug導致錢取不出來,修復迭代就可以,但是在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存儲了總共有多少人買了NFT。

再講一下項目方為用戶退款的過程,項目方要先點擊一個叫processRefunds的按鈕開啟退款,這個按鈕背後的邏輯是把所有出價的用戶全部循環處理一次,循環的次數就是剛才說的存儲出價人數的bidIndex。處理的內容是先判斷該用戶finalProcess退款狀態是否為0,0表示尚未退款,如果為0的話繼續向下執行,將用戶當時的報價減去最低成交價,再乘以購買數量,則等於要退給用戶的差價refund。
然後將該finalProcess用戶退款從0設置為1,表示已經完成退款,從而該用戶不能再去退了。
參數refundProgress是記錄完成退款人數的,每退完一個用戶就會加1,因為是按照出價人數bidIndex循環的,所以refundProgress和bidIndex是一致的,這裡其實沒有毛病,本來出價的人和退款的人就應該是一樣的,但是!接著向下看!

項目方提款的邏輯是怎樣的,又有什麼漏洞導致其無法提款?
下列為項目方進行提款的函數,即當項目方點擊claimProjectFunds按鈕後可以將錢提到自己錢包裡,這裡有三層校驗:第一層是先驗證當前是否已經結束了拍賣,如果結束進入第二層校驗退款人數是否大於報價人數,其實這裡項目方是好意,因為要確保每個人都退完了錢,項目方再提款,但就是這一層校驗出了問題,不知道你還記不記得totalBids是什麼意思?是售出NFT數量呀,不是報價人數!

你會問那這又怎麼了呢?一個人在報價的時候是可以購買多個NFT的呀,退款人數實際上是購買人數,你要求購買人數超過賣出NFT數量,但是每人又可以買多個,那隻要有1個人買了2個,就意味著購買人數永遠不可能大於賣出數量,10個人賣出了11個,你怎麼要求10大於11呢?
我們上etherscan看一下,refundProgess的數量是3699,說明共有3699人報價,但是totalBids的數量是5495,即共賣出了5495個,遠遠超過3699,這輩子refundProgess都不可能大於totalBids,這2個億就永遠被鎖死在了合約中供後人觀摩。


所以是項目方寫錯單詞了,本來應該是想寫bidIndex購買人數,結果寫成了totalBids賣出數量,一個單詞價值2個億,這應該是全世界最貴的一個單詞了,大家給我狠狠的記住這個單詞totalBids,就是它值2個億!
通過這篇文章帶著大家學習了一種新的mint方式荷蘭拍以及其原理,另外帶大家認識了一個2個億的單詞totalBids。
歡迎加入深潮 TechFlow 官方社群
Telegram 訂閱群:https://t.me/TechFlowDaily
Twitter 官方帳號:https://x.com/TechFlowPost
Twitter 英文帳號:https://x.com/BlockFlow_News














