defi-amm-security

defi-amm-security

熱門

Solidity AMM 合約、流動性池及兌換流程的安全性檢查清單。涵蓋重入攻擊、CEI 順序、捐贈或通貨膨脹攻擊、預言機操縱、滑點、管理員控制及整數運算。

23萬星標
3.5萬分支
更新於 2026/7/23
SKILL.md
唯讀
名稱
defi-amm-security
描述

Solidity AMM 合約、流動性池及兌換流程的安全性檢查清單。涵蓋重入攻擊、CEI 順序、捐贈或通貨膨脹攻擊、預言機操縱、滑點、管理員控制及整數運算。

版本
1.0.0

DeFi AMM 安全性

Solidity AMM 合約、LP 金庫及兌換函式的關鍵漏洞模式與強化實作。

使用時機

  • 撰寫或稽核 Solidity AMM 或流動性池合約
  • 實作持有代幣餘額的 swap、deposit、withdraw、mint 或 burn 流程
  • 審查任何在份額或儲備計算中使用 token.balanceOf(address(this)) 的合約
  • 在 DeFi 協議中新增費用設定者、暫停功能、預言機更新或其他管理員功能

運作方式

將此作為檢查清單加模式庫使用。針對以下類別審查每個使用者進入點,並優先使用強化範例而非自行實作。

執行安全

此技能中的 shell 指令為本機稽核範例。請僅在受信任的檢出目錄或一次性沙盒中執行,且不要將未受信任的合約名稱、路徑、RPC URL、私鑰或使用者提供的旗標拼接至 shell 指令。安裝工具或執行可能消耗大量本機或付費資源的長時間模糊測試/靜態分析任務前,請先詢問。

切勿在指令範例、日誌或報告中包含機密資訊、私鑰、助記詞、API token 或主網簽署憑證。

範例

重入攻擊:強制 CEI 順序

有漏洞:

function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    token.transfer(msg.sender, amount);
    balances[msg.sender] -= amount;
}

安全:

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;

function withdraw(uint256 amount) external nonReentrant {
    require(balances[msg.sender] >= amount, "餘額不足");
    balances[msg.sender] -= amount;
    token.safeTransfer(msg.sender, amount);
}

當已有強化函式庫時,請勿自行撰寫防護機制。

捐贈或通貨膨脹攻擊

直接使用 token.balanceOf(address(this)) 進行份額計算,會讓攻擊者透過非預期路徑向合約發送代幣來操控分母。

// 有漏洞
function deposit(uint256 assets) external returns (uint256 shares) {
    shares = (assets * totalShares) / token.balanceOf(address(this));
}
// 安全
uint256 private _totalAssets;

function deposit(uint256 assets) external nonReentrant returns (uint256 shares) {
    uint256 balBefore = token.balanceOf(address(this));
    token.safeTransferFrom(msg.sender, address(this), assets);
    uint256 received = token.balanceOf(address(this)) - balBefore;

    shares = totalShares == 0 ? received : (received * totalShares) / _totalAssets;
    _totalAssets += received;
    totalShares += shares;
}

追蹤內部會計並測量實際收到的代幣數量。

預言機操縱

即時價格可被閃電貸操縱。建議使用 TWAP。

uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = 1800;
secondsAgos[1] = 0;
(int56[] memory tickCumulatives,) = IUniswapV3Pool(pool).observe(secondsAgos);
int24 twapTick = int24(
    (tickCumulatives[1] - tickCumulatives[0]) / int56(uint56(30 minutes))
);
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(twapTick);

滑點保護

每個兌換路徑都需要呼叫者提供的滑點與截止時間。

function swap(
    uint256 amountIn,
    uint256 amountOutMin,
    uint256 deadline
) external returns (uint256 amountOut) {
    require(block.timestamp <= deadline, "已過期");
    amountOut = _calculateOut(amountIn);
    require(amountOut >= amountOutMin, "滑點超出限制");
    _executeSwap(amountIn, amountOut);
}

安全的儲備計算

import {FullMath} from "@uniswap/v3-core/contracts/libraries/FullMath.sol";

uint256 result = FullMath.mulDiv(a, b, c);

對於大型儲備計算,當存在溢位風險時,避免使用簡單的 a * b / c

管理員控制

import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";

contract MyAMM is Ownable2Step {
    function setFee(uint256 fee) external onlyOwner { ... }
    function pause() external onlyOwner { ... }
}

所有權轉移應採用明確接受機制,並對每個特權路徑設置閘門。

安全檢查清單

  • 暴露重入風險的進入點使用 nonReentrant
  • 遵守 CEI 順序
  • 份額計算不依賴原始 balanceOf(address(this))
  • ERC-20 轉帳使用 SafeERC20
  • 存款時測量實際收到的代幣
  • 預言機讀取使用 TWAP 或其他抗操縱來源
  • 兌換需要 amountOutMindeadline
  • 易溢位的儲備計算使用安全原語如 mulDiv
  • 管理員函式有權限控制
  • 存在緊急暫停功能並經過測試
  • 上線前執行靜態分析與模糊測試

稽核工具

pip install slither-analyzer
slither . --exclude-dependencies

echidna-test . --contract YourAMM --config echidna.yaml

forge test --fuzz-runs 10000