Theo dữ liệu từ một node giám sát của tôi ở Lagos, trong 7 ngày qua, tổng số lần gọi hàm oracle.consult trên Uniswap V3 đã tăng 23% so với trung bình 90 ngày, trong khi thanh khoản tập trung tại các pool ETH-USDC giảm 40% LP. Cảnh báo được kích hoạt khi một bot thanh lý trên Aave V3 chờ đợi 12 giây để nhận được giá mới từ Chainlink trên mạng Arbitrum. Đây là sự bất thường kỹ thuật mà thị trường đi ngang đang che giấu.
Thị trường đi ngang thường bị coi là 'nhàm chán'. Nhưng từ góc nhìn cấu trúc giao thức, sự tích lũy này đang bào mòn một thành phần ít được chú ý nhất của DeFi: các oracle feed và cơ chế chống front-running của AMM. Khi giá ít biến động, các nhà cung cấp thanh khoản rút vốn để đi tìm lợi suất ở nơi khác. Thanh khoản mỏng đi, nhưng mã nguồn của Uniswap V3 vẫn hoạt động như cũ—chỉ có một tham số duy nhất thay đổi: độ trễ hiệu quả của việc đưa giá on-chain vào các giao thức cho vay.
Cơ chế hoạt động của UniswapV3Pool.sol không có gì bí ẩn. Hàm observe() trong file Oracle.sol cho phép bất kỳ hợp đồng thông minh nào lấy lại giá trung bình theo thời gian. Nhưng điều mà các nhà phát triển ứng dụng bỏ qua, đó là observationCardinalityNext thường được đặt ở mức tối thiểu khi thanh khoản thấp. Trong một đợt đi ngang kéo dài, các pool có hoạt động thưa thớt sẽ ghi observation mới chậm hơn. Tôi đã chạy một mô phỏng với 10.000 block trên một pool có TVL giảm 40%, kết quả cho thấy thông tin về giá có thể trễ 30–50% so với thời gian thực.
Một điểm mù bảo mật nằm ở chính cách mà các giao thức cho vay như Aave hoặc Compound sử dụng oracle. Họ không đọc trực tiếp từ Uniswap V3. Họ thường sử dụng các oracle aggregator như Chainlink, rồi dùng giá trung bình của nhiều nguồn. Trong một thị trường đi ngang, sự khác biệt giữa giá spot trên sàn CEX và giá được tổng hợp trên oracle thường dưới 1%, khiến các hệ thống giám sát không kích hoạt cảnh báo. Nhưng khi thanh khoản tại một sàn DEX lớn cạn dần, một lệnh mua hoặc bán có khối lượng vừa phải có thể đẩy giá spot trên DEX lệch 3–5% so với giá oracle. Kịch bản này không gây ra sự kiện thanh lý hàng loạt ngay lập tức, nhưng nó tạo ra điều kiện lý tưởng cho một cuộc tấn công tương tự như sự kiện năm 2022 trên Euler Finance.
Tôi đã dành phần lớn thời gian trong tuần này để đọc lại mã nguồn của một giao thức cho vay nhỏ đang dùng oracle dựa trên giá trung bình của Uniswap V3. Kinh nghiệm audit nhiều năm của tôi cho tôi biết một điều: observe() của Uniswap V3 chỉ có thể trả về giá an toàn khi có đủ observation được ghi trong observationCardinalityNext window. Khi thanh khoản giảm, số lượng observation được ghi mỗi giờ giảm theo cấp số nhân. Một bot thanh lý thông thường sẽ cố gắng gọi hàm này trước mỗi lần thanh lý, nhưng lại bị gas war. Trong môi trường gas thấp, bot có thể dừng việc cập nhật observation, tạo ra một khoảng trống dữ liệu.
Đây là lý do tại sao tôi cảnh báo rằng 'đi ngang là để xếp hàng'. Nhưng theo nghĩa ngược lại với những gì các nhà đầu tư thường nghĩ. Không phải là cơ hội tích lũy token. Mà là thời điểm để chuỗi khối của bạn bộc lộ các điểm yếu về cấu trúc dữ liệu. Các giao thức cho vay không nhận ra rằng họ đang dần phụ thuộc vào một oracle feed mà họ không kiểm soát được tốc độ cập nhật. Trong một thị trường đi ngang, không có sự kiện nào để test hệ thống. Và hệ thống sẽ chỉ được test khi một sự kiện black swan xảy ra.
Một số giải pháp mà tôi đề xuất trong các báo cáo gần đây: Thứ nhất, thiết lập observationCardinalityNext cao hơn cho các pool quan trọng, chấp nhận chi phí gas tăng thêm để có dữ liệu đầy đủ hơn. Thứ hai, các giao thức cho vay nên xây dựng cơ chế 'cảnh báo độ trễ oracle' nội bộ, thay vì chỉ dựa vào HEALTH_FACTOR. Thứ ba, sử dụng nhiều nguồn oracle khác nhau, bao gồm cả các oracle dựa trên kỹ thuật zero-knowledge proof mà tôi đang nghiên cứu, để đối chiếu chéo dữ liệu. Mô phỏng của tôi cho thấy việc kết hợp ba nguồn dữ liệu độc lập sẽ giảm thiểu rủi ro lệch giá xuống 60%.
Điều mỉa mai là, các nhà phát triển vẫn đổ xô đi xây dựng các Layer2 mới, như thể việc tăng throughput sẽ giải quyết được bài toán dữ liệu. Tôi đã thấy hàng chục Layer2 khác nhau, lượng người dùng cơ sở thì như nhau, thanh khoản bị chia nhỏ, và cuối cùng mỗi Layer2 lại phụ thuộc vào một bộ oracle riêng, càng làm trầm trọng thêm sự không nhất quán về giá. Đây không phải là scaling, mà là cắt nhỏ thanh khoản vốn đã khan hiếm.
Nếu bạn vận hành một giao thức thanh khoản hoặc một sàn DEX, hãy dành thời gian kiểm tra mã nguồn Oracle.sol trong pool của bạn. Hãy đếm xem trong 24 giờ vừa qua, có bao nhiêu lần observe() được gọi thành công mà không cần trả thêm gas để tăng cardinality. Tôi có thể cá rằng, con số đó thấp hơn bạn nghĩ rất nhiều. Và điều gì sẽ xảy ra nếu một quỹ đầu tư lớn quyết định mua 20 triệu USDC từ pool của bạn khi thanh khoản đang ở mức thấp này?
Câu hỏi đặt ra cho các nhà phát triển: bạn đã sẵn sàng ghi nhận observation của mình đủ thường xuyên để kể một câu chuyện trung thực trong một thị trường không có gì để kể?