DeFi AMM Sicherheit
Kritische Schwachstellenmuster und gehärtete Implementierungen für Solidity AMM-Verträge, LP-Vaults und Swap-Funktionen.
Wann zu verwenden
- Schreiben oder Auditieren eines Solidity AMM- oder Liquiditätspool-Vertrags
- Implementieren von Swap-, Einzahlungs-, Auszahlungs-, Mint- oder Burn-Flows, die Token-Salden halten
- Überprüfen eines Vertrags, der
token.balanceOf(address(this))in der Anteils- oder Reserve-Mathematik verwendet - Hinzufügen von Gebührensetzern, Pausierern, Orakel-Updates oder anderen Admin-Funktionen zu einem DeFi-Protokoll
Wie es funktioniert
Verwenden Sie dies als Checklisten-plus-Muster-Bibliothek. Überprüfen Sie jeden Benutzer-Einstiegspunkt anhand der untenstehenden Kategorien und bevorzugen Sie die gehärteten Beispiele gegenüber handgerollten Varianten.
Ausführungssicherheit
Die Shell-Befehle in dieser Fähigkeit sind lokale Audit-Beispiele. Führen Sie sie nur in einem vertrauenswürdigen Checkout oder einer Einweg-Sandbox aus und setzen Sie keine nicht vertrauenswürdigen Vertragsnamen, Pfade, RPC-URLs, privaten Schlüssel oder vom Benutzer bereitgestellte Flags in Shell-Befehle ein. Fragen Sie, bevor Sie Tools installieren oder langwierige Fuzzing/Static-Analyse-Jobs ausführen, die erhebliche lokale oder kostenpflichtige Ressourcen verbrauchen könnten.
Fügen Sie niemals Geheimnisse, private Schlüssel, Seed-Phrasen, API-Token oder Mainnet-Signatur-Anmeldeinformationen in Befehlsbeispiele, Protokolle oder Berichte ein.
Beispiele
Reentrancy: CEI-Reihenfolge erzwingen
Anfällig:
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
token.transfer(msg.sender, amount);
balances[msg.sender] -= amount;
}
Sicher:
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, "Unzureichend");
balances[msg.sender] -= amount;
token.safeTransfer(msg.sender, amount);
}
Schreiben Sie keinen eigenen Guard, wenn eine gehärtete Bibliothek existiert.
Spenden- oder Inflationsangriffe
Die direkte Verwendung von token.balanceOf(address(this)) für die Anteils-Mathematik ermöglicht es Angreifern, den Nenner zu manipulieren, indem sie Tokens außerhalb des beabsichtigten Pfads an den Vertrag senden.
// Anfällig
function deposit(uint256 assets) external returns (uint256 shares) {
shares = (assets * totalShares) / token.balanceOf(address(this));
}
// Sicher
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;
}
Verfolgen Sie die interne Buchhaltung und messen Sie tatsächlich erhaltene Tokens.
Orakel-Manipulation
Spot-Preise sind durch Flash-Loans manipulierbar. Bevorzugen Sie 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);
Slippage-Schutz
Jeder Swap-Pfad benötigt vom Aufrufer bereitgestellten Slippage und eine Frist (Deadline).
function swap(
uint256 amountIn,
uint256 amountOutMin,
uint256 deadline
) external returns (uint256 amountOut) {
require(block.timestamp <= deadline, "Abgelaufen");
amountOut = _calculateOut(amountIn);
require(amountOut >= amountOutMin, "Slippage überschritten");
_executeSwap(amountIn, amountOut);
}
Sichere Reserve-Mathematik
import {FullMath} from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
uint256 result = FullMath.mulDiv(a, b, c);
Vermeiden Sie bei großer Reserve-Mathematik naive a * b / c, wenn Überlaufrisiko besteht.
Admin-Kontrollen
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
contract MyAMM is Ownable2Step {
function setFee(uint256 fee) external onlyOwner { ... }
function pause() external onlyOwner { ... }
}
Bevorzugen Sie explizite Annahme für die Eigentumsübertragung und schützen Sie jeden privilegierten Pfad.
Sicherheitscheckliste
- Reentrancy-exponierte Einstiegspunkte verwenden
nonReentrant - CEI-Reihenfolge wird eingehalten
- Anteils-Mathematik hängt nicht von rohem
balanceOf(address(this))ab - ERC-20-Transfers verwenden
SafeERC20 - Einzahlungen messen tatsächlich erhaltene Tokens
- Orakel-Lesungen verwenden TWAP oder eine andere manipulationsresistente Quelle
- Swaps erfordern
amountOutMinunddeadline - Überlaufempfindliche Reserve-Mathematik verwendet sichere Primitive wie
mulDiv - Admin-Funktionen sind zugriffsgesteuert
- Es gibt eine Notfall-Pause und diese wird getestet
- Statische Analyse und Fuzzing werden vor der Produktion durchgeführt
Audit-Tools
pip install slither-analyzer
slither . --exclude-dependencies
echidna-test . --contract YourAMM --config echidna.yaml
forge test --fuzz-runs 10000