Khi tôi mở block explorer của VietSwap vào lúc 2 giờ sáng, số dư 0 dòng trên pool USDT/VIC hiện ra như một lời thách thức. Không có gì. Không một giọt thanh khoản. Ai đó đã rút sạch pool chỉ trong 2 block. Đây không phải lần đầu tiên tôi thấy điều này, nhưng mỗi lần đều gợi lại cảm giác lạnh sống lưng của Confido năm 2017.

VietSwap là một AMM clone của Uniswap V2, do team dev Việt Nam xây dựng, huy động 500.000 USD từ cộng đồng thông qua IDO trên một launchpad nội địa. Họ tự hào về tính phi tập trung và tốc độ giao dịch nhanh trên sidechain Viction (trước đây là TomoChain). Whitepaper viết bằng tiếng Việt rất dễ hiểu, roadmap rõ ràng, và họ đã ký hợp đồng audit với một công ty nhỏ đến từ Đông Âu – loại công ty mà tôi biết đôi khi chỉ kiểm tra mã nguồn qua loa. Tuy nhiên, bản thân việc có audit không đảm bảo gì cả. Tôi đã thấy nhiều dự án 'đã được audit' vẫn sụp đổ.
Sáng hôm sau, team VietSwap lên tiếng: 'Chúng tôi đã bị tấn công flash loan. Thiệt hại ước tính 200.000 USD. Đang phối hợp với các sàn để truy vết.' Câu nói quen thuộc. Một lần nữa, lỗi lại xuất hiện ở nơi ít ai ngờ tới. Tôi quyết định tự mình mổ xẻ smart contract của họ – phiên bản đã deploy trên Viction. Tôi clone repo từ GitHub (mã nguồn mở, họ công khai đúng cam kết). Chỉ mất 30 phút để tìm ra vấn đề.
Lỗi nằm ở hàm swap trong contract chính. Cụ thể, họ override hàm swap của Uniswap V2 nhưng quên kiểm tra số dư pool trước khi cập nhật reserve. Dòng code như sau:
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external override {
// ...
_update(balance0, balance1, reserve0, reserve1);
// ...
}
Trong Uniswap V2 gốc, _update được gọi sau khi chuyển token, nhưng ở VietSwap, họ gọi _update trước khi chuyển token. Điều này cho phép một flash loan kẻ tấn công vay lượng lớn token từ pool khác, gọi swap nhiều lần trong cùng một giao dịch, làm sai lệch reserve tạm thời, và rút toàn bộ thanh khoản trước khi _update kịp phát hiện. Họ gọi đó là hack, tôi gọi đó là sơ hở được khai thác.
Tôi kiểm tra thêm: contract của họ không có reentrancy guard, không kiểm tra min return amount. Một lỗi mà bất kỳ dev Solidity nào cũng học được trong tuần đầu tiên. Nhưng team VietSwap – những người đã huy động nửa triệu đô – lại bỏ qua nó. Tại sao? Vì họ vội vàng ra mắt để bắt kịp trend? Vì audit công ty kia chỉ check surface? Hay vì họ tin rằng 'clone từ Uniswap thì an toàn'?
Người ta thường nói 'của thiên trả địa', với smart contract thì 'của thiên trả hacker'. Nhưng sự thật là không có hacker nào xa lạ cả. Chỉ là một kẻ cơ hội đọc được code và nhấn nút exploit. Trách nhiệm thuộc về ai? Team dev thiếu kinh nghiệm? Công ty audit bỏ sót? Hay cộng đồng đã FOMO mà không đọc báo cáo audit?
Tôi biết có người sẽ nói: 'Đây là bài học cho cộng đồng, giúp nâng cao nhận thức bảo mật. VietSwap sẽ học hỏi và rebuild tốt hơn.' Đó là góc nhìn phe bò. Nhưng theo góc nhìn của tôi, không có cơ chế bồi thường thì lòng tin sẽ tan biến. Cộng đồng Việt Nam vốn đã hoài nghi với các dự án nội địa sau những vụ scam rúng động. VietSwap có thể khởi động lại, nhưng người dùng đã mất tiền sẽ không quay lại. Họ sẽ chuyển sang các giao thức nước ngoài an toàn hơn, nơi audit không chỉ là một dòng chữ trên website.
Một lần nữa, tôi ngồi trước màn hình, ghi lại toàn bộ phân tích và gửi lên GitHub. Team VietSwap liên hệ tôi, cảm ơn nhưng không có kế hoạch bồi thường. Họ nói 'chúng tôi sẽ phát hành token mới và airdrop cho nạn nhân'. Tôi biết kịch bản này: token mới sẽ rớt giá, cộng đồng lại lỗ thêm. Đã đến lúc cộng đồng crypto Việt Nam ngừng ủng hộ những dự án 'build nhanh, audit rẻ'. Hãy đầu tư vào những dự án mà team sẵn sàng trả phí audit với các công ty uy tín như OpenZeppelin, Trail of Bits, hoặc ít nhất là công ty có track record rõ ràng.
Liệu sau vụ này, VietSwap có đủ khả năng phục hồi hay sẽ trở thành một cái tên khác trong danh sách các dự án Việt Nam chết yểu? Câu trả lời nằm ở hành động tiếp theo của họ. Nhưng trước mắt, tôi vừa mất thêm một đêm để phân tích một lỗi mà lẽ ra không nên tồn tại.