defi-amm-security

defi-amm-security

熱門

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

23萬星標
3.5萬分支
更新於 2026/7/23
SKILL.md
readonlyread-only
name
defi-amm-security
description

Security checklist for Solidity AMM contracts, liquidity pools, and swap flows. Covers reentrancy, CEI ordering, donation or inflation attacks, oracle manipulation, slippage, admin controls, and integer math.

version
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