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 或其他抗操縱來源
- 兌換需要
amountOutMin與deadline - 易溢位的儲備計算使用安全原語如
mulDiv - 管理員函式有權限控制
- 存在緊急暫停功能並經過測試
- 上線前執行靜態分析與模糊測試
稽核工具
pip install slither-analyzer
slither . --exclude-dependencies
echidna-test . --contract YourAMM --config echidna.yaml
forge test --fuzz-runs 10000






