
Một từ đơn gây ra "vụ án mạng": 200 triệu USD trong NFT Akutar bị khóa vĩnh viễn
Tuyển chọn TechFlowTuyển chọn TechFlow

Một từ đơn gây ra "vụ án mạng": 200 triệu USD trong NFT Akutar bị khóa vĩnh viễn
Qua bài viết này, chúng ta đã cùng nhau tìm hiểu một phương pháp mint mới là Dutch auction (đấu giá kiểu Hà Lan) cùng nguyên lý hoạt động của nó, đồng thời làm quen với một thuật ngữ có giá trị lên tới 200 triệu: totalBids.
Tác giả bài viết là Trần Kiếm Jason, đến từ công chúng WeChat “Hôm nay đã hiểu thế giới này rõ hơn một chút chưa?”, bạn đọc có thể theo dõi公众号 để xem các bài viết trước đó và trao đổi sâu hơn với tác giả, hoặc theo dõi Twitter của tác giả: jason_chen998 để cập nhật tin tức mới nhất.
Bài trước viết vềbài viết về NBAvốn đã rất mệt, tổn hao tinh thần, định nghỉ ngơi một thời gian rồi mới tiếp tục viết, nhưng thế giới web3 quá hấp dẫn, mỗi ngày đều có quá nhiều tin lớn, cuối tuần cũng đành bất đắc dĩ phải “làm thêm”.

Hôm nay, một dự án NFT tên Akutar do lỗi hợp đồng khiến 11.539 ETH, tương đương 34 triệu USD, 200 triệu Nhân dân tệ bị khóa vĩnh viễn, không thể rút ra — 200 triệu đấy!
Chúng ta hãy mở địa chỉ hợp đồng ra xem số tiền 200 triệu này cho đỡ thèm, tưởng tượng cảnh đội ngũ Akutar ôm đầu khóc ròng trước dãy số này.

Trước tiên, giới thiệu sơ qua về Akutar. Từ mô tả trên website chính thức và Twitter của họ có thể thấy đây không phải dự án "rác", ngược lại là một dự án chất lượng cao, được đầu tư kỹ lưỡng, dù là phong cách hình ảnh hay chất lượng mô tả lộ trình đều rất tốt.

Người sáng lập là vận động viên bóng chày nổi tiếng Micha Johnson, khởi nguồn từ việc ông vô tình nghe được cuộc trò chuyện giữa một cậu bé da màu và người mẹ. Cậu bé hỏi mẹ rằng liệu một phi hành gia có thể là người da màu không, vì vậy Micha Johnson quyết định phát hành loạt hình ảnh những cậu bé da màu đội mũ bảo hiểm mơ ước trở thành phi hành gia — một câu chuyện khá ý nghĩa.

Vậy tại sao NFT đằng sau câu chuyện ấm áp như vậy lại sụp đổ? Về cơ bản, phần nào là do đội ngũ dự án tham lam kiếm tiền hơn là thực hiện mục tiêu thiện nguyện, tự mình đào hố chôn mình, bởi vì họ sử dụng phương thức đấu giá Hà Lan đặc biệt.
Phương thức đấu giá truyền thống là đặt mức giá thấp, sau đó mọi người trả giá tăng dần, người trả giá cao nhất sẽ mua được — gọi là đấu giá kiểu Anh. Đấu giá kiểu Hà Lan thì ngược lại: đặt mức giá cao nhất ban đầu, sau đó giảm dần, ai chấp nhận ở mức giá nào thì mua ở mức đó. Đấu giá Hà Lan thử thách tâm lý con người hơn, vì ai cũng muốn chờ giá thấp nhất, nhưng lại sợ người khác mua trước.
Azuki từng dùng hình thức đấu giá Hà Lan, nhưng Akutar lại thay đổi so với Azuki: Azuki có giá giảm động, càng mua muộn giá càng thấp, mua sớm có thể chịu thiệt. Akutar thì thêm quy tắc “hoàn tiền”: quy tắc này trông có vẻ thân thiện với người dùng hơn, nhưng tôi cho rằng thực tế là để moi thêm tiền.
Như hình dưới đây, giá khởi điểm đấu giá là 3.5 ETH, cứ sau 6 phút giảm 1 ETH, người mua ở mức giá thấp nhất cuối cùng sẽ xác định mức giá chuẩn. Những người mua với giá cao hơn sẽ được hoàn lại khoản chênh lệch. Ví dụ, nếu giá bán thấp nhất cuối cùng là 1.5 ETH, tất cả những ai trả giá cao hơn 1.5 ETH sẽ được hoàn lại phần chênh lệch. Cơ chế này thực tế nhằm khuyến khích người dùng mạnh dạn mua mà không cần đợi giá thấp nhất, vì dù mua cao cũng sẽ được hoàn tiền.

Do đó, Akutar sẽ có một quỹ lớn lưu trữ toàn bộ tiền người dùng nộp vào, bao gồm cả phần thuộc về đội dự án và phần cần hoàn lại cho người dùng. Trước tiên, ta cần hiểu một kiến thức: như đã đề cập trong các bài trước, bản chất của hợp đồng thông minh giống như ví cá nhân của bạn — đều là một địa chỉ blockchain, có thể nhận và gửi tiền mã hóa. Khi bạn mint một dự án, thực chất là bạn chuyển tiền vào địa chỉ hợp đồng, sau đó hợp đồng gửi lại cho bạn một NFT. Tức là tất cả các đợt phát hành thị trường sơ cấp NFT, tiền đều vào địa chỉ hợp đồng trước, sau đó đội dự án mới thực hiện rút tiền, chuyển số tiền trong hợp đồng về ví của họ.
Lần này, 200 triệu bị khóa chết là do lỗi xảy ra ở bước rút tiền. Vì đặc tính bất biến của hợp đồng thông minh blockchain, khi xuất hiện lỗi thì không thể sửa được. Nếu trong internet truyền thống có bug khiến tiền không rút được, thì có thể sửa và cập nhật phiên bản, nhưng trong web3, điều đó có nghĩa là cả đời bạn chỉ có thể nhìn xa xăm vào 200 triệu kia mà thôi.
Chúng ta hãy cùng xem một số đoạn mã quan trọng đã làm gì để hiểu rõ nguyên lý, rồi phân tích xem vấn đề nằm ở đâu.
Trước tiên tìm hiểu nguyên lý đấu giá Hà Lan: đầu tiên lấy thời gian khối hiện tại block.timestamp, sau đó lấy thời gian hiện tại trừ đi thời gian bắt đầu startAt rồi chia cho 6 (vì cứ 6 phút giảm giá một lần), từ đó tính được số lần giảm giá timeElapsed, nhân số lần này với mức giảm giá để tính tổng mức giảm discount, cuối cùng lấy giá khởi điểm startingPrice trừ đi mức giảm này để có giá hiện tại cần thanh toán. Trong mã, các tham số liên quan đến tiền vừa nói thực ra không được viết cứng trong hợp đồng, mà là các biến có thể chỉnh sửa, chứng tỏ đội dự án để lại cửa hậu, có thể tùy tình hình điều chỉnh giá để dễ dàng “vung dao” hơn.

Đã hiểu cách tính giá, giờ xem quá trình người dùng trả giá diễn ra điều gì. Đoạn mã này dài quá nên tôi không dán hết, chỉ chọn phần quan trọng.
Trước tiên lấy giá hiện tại như đã nói, nhân với số lượng người dùng muốn mua amount, được tổng giá cần thanh toán totalPrice, sau đó kiểm tra xem số tiền người dùng thực sự trả value có lớn hơn tổng giá hay không. Nếu lớn hơn, tức là đủ tiền, tiếp tục thực thi.

Ở đây định nghĩa một báo giá bid gồm những gì: địa chỉ người báo giá bidder, giá cụ thể price, tổng số lượng đã đặt bidsPlaced, và trạng thái hoàn tiền finalProcess (0: chưa hoàn, 1: đã hoàn, 2: hủy hoàn).

Đây là chỗ đầu tiên chôn bom: totalBids biểu thị số lượng NFT đã bán ra, mặc định là 0, mỗi khi có người dùng báo giá thì cộng thêm số lượng họ mua amount. Ghi nhớ chỗ này, lát nữa sẽ dùng đến.

Sau đó chôn bom thứ hai: dùng tham số bidIndex để lưu số người đã báo giá. Ghi nhớ hai tham số này: totalBids lưu tổng số NFT đã bán, bidIndex lưu tổng số người đã mua NFT.

Giờ nói đến quá trình đội dự án hoàn tiền cho người dùng: đội dự án phải nhấn nút “processRefunds” để bắt đầu hoàn tiền. Logic đằng sau nút này là duyệt vòng qua tất cả người đã báo giá, số lần lặp bằng bidIndex — số người đã báo giá. Nội dung xử lý: trước tiên kiểm tra trạng thái hoàn tiền finalProcess của người dùng có bằng 0 không (0 là chưa hoàn), nếu đúng thì tiếp tục, lấy giá lúc họ báo trừ đi giá giao dịch thấp nhất, nhân với số lượng mua, kết quả là khoản hoàn refund.
Sau đó đặt finalProcess của người dùng từ 0 thành 1, biểu thị đã hoàn tiền xong, để người này không thể hoàn lại lần nữa.
Tham số refundProgress ghi nhận số người đã hoàn tiền, mỗi khi hoàn cho một người thì tăng lên 1. Vì duyệt theo số người báo giá bidIndex, nên refundProgress và bidIndex phải bằng nhau. Về mặt logic đây không sai, vì số người báo giá và số người hoàn tiền lẽ ra phải giống nhau. Nhưng! Hãy xem tiếp!

Logic rút tiền của đội dự án như thế nào, và lỗi gì khiến họ không thể rút?
Dưới đây là hàm rút tiền của đội dự án — khi họ nhấn nút claimProjectFunds thì có thể chuyển tiền về ví cá nhân. Có ba lớp kiểm tra: lớp đầu tiên kiểm tra xem đấu giá đã kết thúc chưa, nếu xong thì vào lớp thứ hai kiểm tra xem số người đã hoàn tiền có lớn hơn số người đã báo giá không. Thực ra đội dự án có ý tốt, muốn đảm bảo mọi người đều được hoàn tiền trước khi họ rút tiền. Nhưng chính lớp kiểm tra này đã gây ra vấn đề. Bạn còn nhớ totalBids là gì không? Đó là số lượng NFT đã bán ra, chứ không phải số người báo giá!

Bạn có thể hỏi: vậy thì sao? Một người khi báo giá có thể mua nhiều NFT mà. Số người hoàn tiền thực chất là số người mua. Bạn yêu cầu số người mua phải lớn hơn số lượng NFT bán ra, nhưng mỗi người có thể mua nhiều cái, vậy chỉ cần một người mua 2 cái, thì số người mua sẽ mãi mãi không thể vượt quá số lượng bán. Ví dụ bán 11 cái cho 10 người, làm sao đòi 10 lớn hơn 11 được?
Chúng ta lên etherscan kiểm tra: refundProgress là 3699, tức là có 3699 người báo giá, nhưng totalBids là 5495, tức là đã bán ra 5495 cái — vượt xa 3699. refunđProgess đời nào cũng không thể lớn hơn totalBids, 200 triệu này sẽ mãi mãi bị khóa trong hợp đồng để người đời sau chiêm ngưỡng.


Tóm lại, đội dự án đã viết sai từ. Họ vốn định viết bidIndex (số người mua), nhưng lại viết thành totalBids (số lượng bán). Một từ đơn trị giá 200 triệu, đây chắc chắn là từ đắt nhất thế giới. Mọi người hãy khắc ghi sâu sắc từ này: totalBids — chính nó đáng giá 200 triệu!
Qua bài viết này, chúng ta đã học được một phương thức mint mới — đấu giá Hà Lan cùng nguyên lý hoạt động, đồng thời biết được một từ đơn trị giá 200 triệu: totalBids.
Chào mừng tham gia cộng đồng chính thức TechFlow
Nhóm Telegram:https://t.me/TechFlowDaily
Tài khoản Twitter chính thức:https://x.com/TechFlowPost
Tài khoản Twitter tiếng Anh:https://x.com/BlockFlow_News














