為什麼滑點容忍度設得太鬆,會直接變成 MEV 三明治攻擊的攻擊面?這兩者具體怎麼連結的?
三明治攻擊的運作原理,本質上就是在利用「你設定的滑點容忍度和實際成交價格之間的落差」,滑點容忍度設得越鬆,這個可被利用的落差就越大,兩者的連結非常直接。
三明治攻擊的具體步驟:攻擊者的機器人持續監控鏈上的待處理交易池(mempool),一旦看到一筆規模夠大、滑點容忍度設定夠鬆的交易(例如你的 Agent 打算用 1 ETH 換某個代幣,滑點容忍度設定 3%),攻擊者會做三件事:先搶在你的交易之前,自己先買入同一個代幣(把價格推高),讓你的交易在一個已經被推高的價格下成交(但只要成交價格還在你設定的 3% 容忍範圍內,交易依然會成功執行,不會失敗),接著攻擊者立刻在你的交易之後,把剛剛買入的代幣賣出(在你的買盤把價格推得更高之後,以更高的價格賣出獲利)。整個過程,你的交易確實成功執行了(因為價格波動還在你設定的容忍範圍內),但你實際拿到的代幣數量,比市場正常價格下應該拿到的少,這個差額,就是攻擊者的獲利來源。
為什麼滑點容忍度是攻擊者鎖定的關鍵參數:攻擊者要讓這個攻擊成功,前提是「你的交易不能因為價格被推高而失敗」——如果滑點容忍度設得很緊(例如 0.1%),攻擊者稍微推高價格,你的交易就會直接 revert 失敗,攻擊者的操縱行為反而白費工夫(甚至可能因為自己的搶跑交易佔用了區塊空間卻沒能從你身上獲利);如果滑點容忍度設得很鬆(例如 3-5%),攻擊者有大量的操縱空間可以把價格推高到接近這個上限,你的交易依然會成功執行,攻擊者穩穩獲利。這代表滑點容忍度的鬆緊,直接決定了「攻擊者對你發動三明治攻擊,值不值得」——容忍度設得越鬆,潛在獲利空間越大,你的交易對攻擊者而言就是越有吸引力的目標。
如果我的 Agent 要根據市場條件動態計算滑點容忍度,具體該參考哪些數據,怎麼設計這個計算邏輯?
動態計算滑點容忍度,核心邏輯是根據「這筆交易在當前市場條件下,合理的價格波動範圍大概是多少」來反推容忍度,而不是套用一個固定的百分比,具體可以參考三個數據維度。
維度一:交易對的即時流動性深度。流動性越深的交易對(例如主流代幣對穩定幣,通常流動池規模很大),同樣金額的交易對價格的衝擊越小,可以設定相對較緊的滑點容忍度;流動性越淺的交易對(例如新上市、交易量小的代幣),同樣金額的交易對價格的衝擊越大,需要設定相對較鬆的容忍度,否則交易很容易失敗。具體做法是,在送出交易前,先即時查詢這個交易對當前的流動池規模,計算「這筆交易金額佔流動池的比例」,比例越高,理論上需要的滑點容忍度也應該越高。
維度二:近期價格波動度(volatility)。可以查詢這個代幣近期(例如過去 1 小時或過去 24 小時)的價格波動幅度,如果近期波動劇烈,代表「交易送出到上鏈之間,價格自然波動的可能性」較高,滑點容忍度應該相應提高,避免因為正常波動而交易失敗;如果近期價格穩定,可以用相對較緊的容忍度,降低被 MEV 攻擊利用的風險。
維度三:交易金額相對於流動池的比例(這點呼應維度一,但更精確)。交易金額越大,對價格的衝擊(price impact)本身就越大,需要的滑點容忍度也該越高——但這裡有一個重要的區別:「交易本身造成的價格衝擊」和「滑點容忍度」是兩個不同的概念,前者是你的交易一定會對價格造成的影響(可以提前精確計算),後者是額外預留給「市場在你交易確認前又發生了新變化」的緩衝空間,滑點容忍度的計算,應該是在「交易本身的價格衝擊」之外,額外加上一個根據波動度計算出來的緩衝值,而不是把兩者混為一談。
具體實作建議:與其自己從零計算這些數據,多數去中心化交易所的介面或 API,通常已經提供了「基於當前市場條件建議的滑點設定值」這個功能,Agent 可以優先查詢並採用這個建議值作為基準,再根據自己的風險偏好做微調(例如在建議值基礎上加一個小的安全邊際),而不是完全自己重新發明一套計算邏輯。
如果 Agent 交易失敗(因為滑點超出容忍度),前面重試策略文章討論過的三種失敗類型,這種失敗屬於哪一種,該怎麼處理?
這種失敗嚴格來說,不完全屬於前面重試策略文章討論的暫時性、結構性、循環性任何一種,需要一個更細緻的判斷邏輯,才能決定該用哪種處理方式,原因是「滑點超出容忍度」的根本原因,可能是暫時性的市場波動,也可能是持續性的市場條件變化,兩者需要不同對策。
判斷方法:重新查詢當前市場條件,看價格是否已經穩定。交易因為滑點失敗後,不要立即用原本的參數重試,而是先重新查詢當前的市場價格和流動性狀況,和送出原本那筆交易時的市場條件做比較。如果重新查詢後,價格已經回到接近原本預期的水準(代表剛才的波動是短暫的、暫時性的),這種情況適合用前面提到的指數退避重試邏輯,用原本或接近原本的滑點設定重新嘗試;如果重新查詢後,價格出現了持續性的、方向明確的移動(例如因為有一筆大額交易永久性地改變了流動池的價格,不是暫時性的波動而是新的價格水準),這種情況不該用原本的參數重試,而應該重新計算一個對應「新的市場條件」的滑點容忍度,或者重新評估這筆交易在新價格下是否還值得執行(如果是套利類型的交易,價格已經大幅變動,原本的套利空間可能已經消失,重試反而可能造成虧損)。
這個判斷邏輯呼應了前面提到的「結構性失敗需要改變策略而非單純重試」原則:滑點失敗如果是因為市場條件發生了實質性變化,單純重試(即使换了更寬鬆的滑點設定)不是在解決問題,而是在盲目地想辦法讓交易「無論如何都要成交」,這可能導致 Agent 在一個已經不利的價格下,依然執行了交易,造成不必要的損失——比較好的處理方式是,如果連續幾次因為滑點失敗且重新查詢後發現市場條件確實持續變化,應該觸發前面重試策略文章提到的降級路徑(例如標記為「市場條件已變化,需要重新評估這筆交易的必要性」,而不是無限制地放寬滑點容忍度硬要成交)。
額外的成本考量:每次因為滑點失敗的交易,即使沒有成功執行,通常仍然會消耗一定的 Gas 費(因為交易被送出、在鏈上執行到 revert 的那一刻,才會失敗,這個過程仍然消耗運算資源),這代表滑點失敗的重試,也應該套用前面提到的「單一任務總成本上限」設計,避免反覆因為滑點失敗重試,累積消耗大量 Gas 費卻始終無法成功執行任何實質操作。
在多 Agent 系統裡,如果 Sub-agent A 負責判斷市場時機、Sub-agent B 負責實際執行交易,滑點容忍度該由誰決定?
這個問題可以套用前面 Handoff 詞條討論過的「執行型 Agent 不應該完全依賴交接封包裡的結論做最終決策」原則,答案是:滑點容忍度的初步建議可以由 Sub-agent A 提供,但最終的實際數值,應該由負責執行的 Sub-agent B 在執行的當下重新驗證和確定,而不是全盤照抄 A 給的數字。
為什麼不能完全由 Sub-agent A 決定:Sub-agent A 判斷市場時機時,依據的是它當下查詢到的市場數據,但從 A 完成判斷、把交易任務透過 Handoff 交給 B、到 B 實際送出交易,中間可能有一段時間差(即使很短)——市場條件在這段時間差裡可能已經有變化,如果 B 直接使用 A 交接封包裡寫的滑點容忍度,而不重新驗證,可能會用一個基於「已經過時的市場快照」計算出來的容忍度去執行交易,這正好呼應了前面 Handoff 詞條提到的「交接封包裡的資訊,在被接手方使用的當下,可能已經不是最新狀態」這個風險。
具體的分工建議:
Sub-agent A(判斷時機) 提供的交接封包裡,除了「建議執行交易」這個結論,還應該附上它做判斷當下的市場快照數據(當時查到的流動性深度、波動度),讓 B 有依據可以判斷「這些數據距離我現在執行的當下,過了多久、是否還適用」。
Sub-agent B(執行交易) 在真正送出交易之前,應該用前面 Q2 提到的動態計算邏輯,基於自己執行當下重新查詢的即時市場數據,獨立計算一次滑點容忍度,而不是直接沿用 A 給的數字。如果 B 重新計算出來的建議值,和 A 原本給的建議值差異很大(代表市場條件在這段時間內發生了顯著變化),這本身是一個值得回頭確認的信號——可以觸發前面提到的「重新評估這筆交易在新條件下是否還該執行」這個邏輯,而不是盲目地用重新計算的滑點容忍度硬是把交易送出去。
這個設計呼應的核心原則:滑點容忍度和其他任何「即時性敏感」的參數一樣,不該被當成一個可以在 Handoff 過程中被動傳遞、原封不動使用的靜態數值,而應該在最接近實際執行的那個時間點,由負責執行的 Agent 重新驗證,這是分散在多個 Agent 之間的任務,對「即時性」要求較高時,特別需要注意的設計細節。
一個跨鏈套利 Agent 動態調整滑點容忍度的實作案例
某個尋找不同鏈上同一代幣價差套利機會的 Agent,滑點容忍度計算邏輯設計如下:
基準計算:每次執行交易前,即時查詢目標流動池的深度和過去 15 分鐘的價格波動標準差,用一個簡化公式估算基礎滑點容忍度:基礎容忍度 = (交易金額 / 流動池規模) × 2 + 波動度係數,其中波動度係數根據過去 15 分鐘標準差動態調整(波動越大,係數越高)。
上限保護:不管基準計算出來的數字是多少,設定一個硬性上限(例如 2%),如果計算出來的建議值超過這個上限,系統會標記「當前市場條件不適合自動化交易」,暫停這筆交易而不是用一個過高的容忍度硬做,這個上限的存在,直接對應前面討論過的「滑點容忍度設得太鬆會變成 MEV 攻擊面」這個風險,用一個絕對上限做最後防線。
執行前重新驗證:套利判斷 Sub-agent 產生交易建議後,交由執行 Sub-agent 處理,執行 Sub-agent 在真正送出交易的前一刻,重新查詢一次市場數據,如果重新計算出來的容忍度和判斷 Sub-agent 給的建議值差異超過 20%,觸發「重新評估套利空間是否仍然存在」的邏輯,而不是直接沿用舊的建議值執行。
實際運作中發現的問題:工程團隊在測試階段發現,某次套利機會判斷完成後,因為 Sub-agent 之間的 Handoff 加上執行 Sub-agent 的排隊延遲,中間間隔了大約 45 秒,這段時間內,該代幣的流動池因為另一筆大額交易被永久性地改變了價格(不是暫時性波動),重新驗證機制成功偵測到這個變化,判斷「原本的套利空間已經消失」,取消了這筆交易,避免了 Agent 在一個已經不再有利可圖的價格下強行執行交易造成虧損。這個案例驗證了「執行前重新驗證」這個設計環節的實際價值——如果沒有這層防護,Agent 會直接沿用 45 秒前的判斷結果,在套利空間已經消失的情況下依然執行交易,造成不必要的損失。
滑點容忍度設計的核心取捨是「交易成功率 vs MEV 攻擊暴露度」。設得越鬆,交易越不容易因為正常波動而失敗,成功率越高,但同時給 MEV 攻擊者的操縱空間也越大;設得越緊,MEV 攻擊者的可乘之機越小,但正常市場波動導致交易失敗的機率也越高,每次失敗都浪費已消耗的 Gas 費。另一個取捨是「動態計算的準確度 vs 計算本身的延遲成本」:動態計算滑點容忍度需要即時查詢市場數據(流動性、波動度),這個查詢過程本身需要時間,如果為了追求最準確的即時數據而花費太多時間查詢,可能導致市場條件在查詢完成後又已經變化,過度追求精確反而增加了「查詢延遲期間市場又變動」的新風險,需要在計算精確度和查詢速度之間找平衡。建議:一般性交易,使用交易所或流動池介面提供的標準建議值加小幅安全邊際,避免自己重新發明容易出錯的計算邏輯;高頻或大額的套利交易,值得投入資源做更精細的動態計算,並搭配前面提到的執行前重新驗證機制,把「查詢延遲期間市場變動」這個新風險,透過在最接近實際執行的時間點做最後確認來降到最低。