swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline) — dòng code này quen thuộc đến mức nhàm. Nhưng tôi cá rằng 80% bot bạn đang chạy ngoài kia set deadline kiểu block.timestamp + 3600. Một giờ. Để làm gì? Để bot của bạn nằm im trong mempool suốt một tiếng đồng hồ chờ người ta front-run? Hay để bạn kịp đi uống cà phê rồi quay lại xem giao dịch đã revert chưa?
Tôi là Bùi Trí, smart contract architect ở Lagos, đã từng xây dựng bot yield farming trên Uniswap v2 vào năm 2020. Hồi đó tôi cũng mắc sai lầm y hệt. Chạy bot 6 tháng, lời 12 ETH nhưng mất 2 ETH vì slippage và deadline không hợp lý. Đau nhất là lần bị sandwich attack mất 0.5 ETH chỉ vì deadline quá dài, cho phép kẻ tấn công có thời gian chèn giao dịch vào giữa. Từ đó tôi rút ra bài học: deadline không chỉ là tham số phòng hờ, nó là lá chắn chính chống lại front-running. Bài viết này sẽ mổ xẻ cơ chế deadline trong Uniswap v2, chỉ ra điểm mù mà 90% bot dev bỏ qua, và đưa ra code mẫu để bạn tự kiểm tra.
Context: Cơ chế AMM và vai trò của deadline
Uniswap v2 là AMM (Automated Market Maker) đơn giản nhất: giá được xác định bởi công thức x * y = k. Khi bạn swap, bạn gửi giao dịch vào mempool, validator chọn lệnh và chạy. Nhưng giữa lúc bạn ký và lúc giao dịch được include, giá có thể thay đổi. Đó là lý do Uniswap có tham số amountOutMin — bạn chỉ chấp nhận trượt giá tối đa X%. Deadline thì sao? Nó cho phép bạn đặt một mốc thời gian tối đa (block timestamp) mà giao dịch phải được thực hiện trước đó. Nếu quá deadline, giao dịch revert. Nghe có vẻ đơn giản, nhưng thực tế lại là nguồn gốc của nhiều vụ mất tiền.
Trong hợp đồng Uniswap v2, deadline được kiểm tra ở dòng: require(block.timestamp <= deadline, 'UniswapV2Router: EXPIRED');. Nếu bạn set deadline quá xa, bạn cho phép giao dịch của mình nằm trong mempool rất lâu. Kẻ tấn công có thể quan sát, tính toán giá hiện tại, và chèn giao dịch sandwich: mua trước bạn, đẩy giá lên, bán lại cho bạn, rồi chốt lời. Nếu deadline quá ngắn, giao dịch của bạn có thể bị revert do network congestion. Cân bằng giữa hai thái cực này là nghệ thuật.
Core: Phân tích code bot và lỗi deadline thường gặp
Dưới đây là code mẫu bot arbitrage Uniswap v2 bằng Python + Web3.py. Tôi sẽ chỉ ra lỗi deadline điển hình.
from web3 import Web3
import time
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_KEY')) router = w3.eth.contract(address='0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', abi=ROUTER_ABI)

def arbitrage(token_in, token_out, amount_in): # Lỗi 1: deadline cứng cách 1 giờ deadline = int(time.time()) + 3600 # 1 hour later
tx = router.functions.swapExactTokensForTokens( amount_in, 0, # amountOutMin = 0 – cực kỳ nguy hiểm [token_in, token_out], w3.eth.default_account, deadline ).build_transaction({ 'from': w3.eth.default_account, 'gas': 200000, 'gasPrice': w3.eth.gas_price }) signed = w3.eth.account.sign_transaction(tx, private_key='0x...') tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction) return tx_hash ```
Nhìn vào dòng deadline: int(time.time()) + 3600. Đây là lỗi kinh điển. Bạn lấy thời gian hệ thống (Unix timestamp) cộng thêm 3600 giây. Nhưng blockchain không dùng thời gian hệ thống; nó dùng block timestamp, là thời gian của validator. Block timestamp có thể chênh lệch vài giây so với thời gian thực, nhưng quan trọng hơn: nếu bạn set deadline quá xa, kẻ tấn công có thể chờ đợi cơ hội thuận lợi. Ví dụ: giá DAI/USDC đang ở 1.00, bot của bạn detect cơ hội arbitrage 0.3% và gửi giao dịch swap. Nếu deadline là 1 giờ, trong 1 giờ đó giá có thể biến động mạnh. Hoặc tệ hơn, một kẻ sandwich sẽ ngay lập tức chèn giao dịch của họ vào trước và sau giao dịch của bạn, vì họ biết bạn sẽ không revert sớm.
Lỗi thứ hai: amountOutMin = 0. Bạn cho phép nhận về 0 token? Điều này kết hợp với deadline dài tạo thành thảm họa: bot của bạn có thể bị sandwich và nhận về gần như không có gì, nhưng giao dịch vẫn thành công vì bạn không đặt giới hạn trượt giá.
Cách fix đúng:
def get_safe_deadline(blocks_ahead=10):
# Lấy block hiện tại, ước lượng thời gian cho 10 blocks (~30 giây)
current_block = w3.eth.block_number
current_ts = w3.eth.get_block(current_block).timestamp
# Thêm 10 blocks, mỗi block ~12-15 giây
return current_ts + 10 * 15 # 150 giây
def arbitrage_fixed(token_in, token_out, amount_in): deadline = get_safe_deadline(blocks_ahead=10) # amountOutMin dựa trên slippage 0.5% amount_out = get_amount_out(amount_in, reserve_in, reserve_out) # từ cặp amount_out_min = int(amount_out * 0.995)
tx = router.functions.swapExactTokensForTokens( amount_in, amount_out_min, [token_in, token_out], w3.eth.default_account, deadline ).build_transaction({...}) ... ```

Ở đây, deadline được tính dựa trên block timestamp hiện tại cộng với thời gian ước lượng để giao dịch được include. Thường là 5-15 blocks tùy vào độ tắc nghẽn mạng. Tôi hay dùng blocks_ahead = 10 cho Ethereum mainnet, tương đương khoảng 2 phút. Đủ để giao dịch được xử lý, nhưng không đủ để kẻ tấn công có thời gian phân tích và phản ứng.
Contrarian: Điểm mù bảo mật mà ít ai để ý
Nhiều người cho rằng deadline chỉ là tham số phụ, không quan trọng bằng amountOutMin. Sai lầm. Thực tế, deadline là cơ chế bảo vệ đầu tiên chống lại front-running. Khi deadline ngắn, bạn giới hạn thời gian kẻ tấn công có thể quan sát và chèn giao dịch. Nếu deadline chỉ 30 giây, kẻ sandwich phải hành động cực nhanh, khả năng thành công thấp hơn. Tuy nhiên, deadline quá ngắn cũng là điểm mù: nếu mạng đột nhiên tắc nghẽn (ví dụ: gas spike), giao dịch của bạn có thể bị revert, gây lãng phí gas. Vậy nên cần điều chỉnh linh hoạt dựa trên tình trạng mạng hiện tại.
Một điểm mù khác: bot thường set deadline block.timestamp + 1000 (tức là một hằng số lớn) để tránh revert. Nhưng điều này vô tình biến bot thành mục tiêu lý tưởng cho sandwich. Tôi đã từng phân tích một bot của đối thủ, thấy họ set deadline block.timestamp + 999999. Kết quả: bot bị sandwich liên tục, mất 30% lợi nhuận. Đừng quên deadline.
Takeaway: Dự báo lỗ hổng trong tương lai
Khi thị trường giảm, bot arbitrage càng hoạt động mạnh để kiếm lời từ biến động nhỏ. Nhưng chính lúc này, deadline sai lầm càng gây hại nặng. Tôi dự đoán trong 6 tháng tới sẽ có ít nhất một vụ hack hoặc sự cố lớn liên quan đến việc bot set deadline không đúng, dẫn đến mất hàng triệu USD. Các giao thức như Uniswap v3 có cơ chế deadline tương tự, nhưng với concentrated liquidity, rủi ro còn cao hơn. Hãy kiểm tra lại bot của bạn ngay hôm nay. Nếu không, bạn sẽ là người tiếp theo bị sandwich.
Còn bạn? Bạn đã kiểm tra deadline của bot mình chưa? Hay bạn vẫn nghĩ block.timestamp + 3600 là ổn?