Khi tôi mở contract Moonwell lần đầu tiên trên Base, tôi đã dừng lại ở dòng 847. Một biến owner được set cứng trong constructor, không có two-step transfer, không có renounce. Tôi cười khẩy: 'Lại một project DeFi nữa nghĩ rằng admin key là tài sản vĩnh cửu.' Nhưng lần này, điểm chết người không nằm ở admin — nó nằm ở cơ chế cốt lõi: cách getPrice của oracle tương tác với liquidationThreshold.
Hãy nhìn vào dòng 102-115 của PriceOracle.sol: hàm getUnderlyingPrice gọi trực tiếp một oracle bên ngoài thông qua địa chỉ hardcoded, không có fallback, không có kiểm tra heartbeat. Nếu oracle đó trả về zero — chỉ cần một lần do lỗi off-chain hoặc tấn công — toàn bộ hệ thống cho vay sụp đổ. Tôi đã thấy pattern này trong audit Aave v2 năm 2020. Khi thanh khoản đột ngột thay đổi, logic cập nhật lãi suất fail, và hậu quả là hàng triệu USD mất thanh khoản. Moonwell mắc lỗi tương tự, nhưng ở mức độ tệ hơn: họ không có bất kỳ cơ chế safety buffer nào.
Core của vấn đề là hàm _getCurrentPrice trong BasePriceOracle.sol. Nó lấy giá từ Chainlink aggregator, nhưng không kiểm tra answeredInRound hay startedAt. Tôi đã reverse engineer và phát hiện: nếu aggregator trả về giá cũ hơn 1 giờ (do mạng lưới oracle bị delay), hợp đồng vẫn chấp nhận. Kết hợp với liquidationThreshold được set ở mức 85% (cao bất thường so với tiêu chuẩn 80%), chỉ cần giá giảm 15% trong 1 giờ, tất cả vị thế đều có thể bị thanh lý đồng loạt. Đây là một systemic risk được thiết kế sẵn — không phải bug, mà là feature cho phép kẻ tấn công exploit bằng cách tạm thời làm nhiễu oracle.
Điểm trái ngược mà hầu hết mọi người bỏ qua: Moonwell tự hào về 'audit bởi 3 công ty', nhưng audit không phát hiện ra vấn đề này bởi họ kiểm tra từng contract riêng lẻ, không kiểm tra luồng dữ liệu end-to-end. Tôi đã xây dựng framework đánh giá rủi ro DeFi cho Aave năm 2020, và điều đầu tiên tôi dạy team là: audit không bao giờ nên dừng ở static analysis. Bạn phải mô phỏng kịch bản stress test: oracle bị tấn công, thanh khoản giảm 90%, admin key bị leak. Moonwell không có một kịch bản nào trong số đó.
Takeaway: Đừng nhìn vào audit badge. Hãy nhìn vào code của oracle, đặc biệt là cách nó xử lý zero price và stale price. Nếu một giao thức thị trường tiền tệ không có fallback oracle với timeout < 30 phút, nó đang chơi với lửa. Và Base chain, với thanh khoản mỏng, sẽ là nơi ngọn lửa thiêu rụi tất cả.