Skill

defi-amm-security

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.

Freerisk: medium
defiammsecuritysolidity

Tools: slither-analyzer

The full skill

— 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. origin: ECC direct-port adaptation version: "1.0.0" — # DeFi AMM Security Critical vulnerability patterns and hardened implementations for Solidity AMM contracts, LP vaults, and swap functions. ## When to Use – Writing or auditing a Solidity AMM or liquidity-pool contract – Implementing swap, deposit, withdraw, mint, or burn flows that hold token balances – Reviewing any contract that uses `token.balanceOf(address(this))` in share or reserve math – Adding fee setters, pausers, oracle updates, or other admin functions to a DeFi protocol ## How It Works Use this as a checklist-plus-pattern library. Review every user entrypoint against the categories below and prefer the hardened examples over hand-rolled variants. ## Examples ### Reentrancy: enforce CEI order Vulnerable: “`solidity function withdraw(uint256 amount) external { require(balances[msg.sender] >= amount); token.transfer(msg.sender, amount); balances[msg.sender] -= amount; } “` Safe: “`solidity 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, "Insufficient"); balances[msg.sender] -= amount; token.safeTransfer(msg.sender, amount); } “` Do not write your own guard when a hardened library exists. ### Donation or inflation attacks Using `token.balanceOf(address(this))` directly for share math lets attackers manipulate the denominator by sending tokens to the contract outside the intended path. “`solidity // Vulnerable function deposit(uint256 assets) external returns (uint256 shares) { shares = (assets * totalShares) / token.balanceOf(address(this)); } “` “`solidity // Safe 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; } “` Track internal accounting and measure actual tokens received. ### Oracle manipulation Spot prices are flash-loan manipulable. Prefer TWAP. “`solidity 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 protection Every swap path needs caller-provided slippage and a deadline. “`solidity function swap( uint256 amountIn, uint256 amountOutMin, uint256 deadline ) external returns (uint256 amountOut) { require(block.timestamp <= deadline, "Expired"); amountOut = _calculateOut(amountIn); require(amountOut >= amountOutMin, "Slippage exceeded"); _executeSwap(amountIn, amountOut); } “` ### Safe reserve math “`solidity import {FullMath} from "@uniswap/v3-core/contracts/libraries/FullMath.sol"; uint256 result = FullMath.mulDiv(a, b, c); “` For large reserve math, avoid naive `a * b / c` when overflow risk exists. ### Admin controls “`solidity import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; contract MyAMM is Ownable2Step { function setFee(uint256 fee) external onlyOwner { … } function pause() external onlyOwner { … } } “` Prefer explicit acceptance for ownership transfer and gate every privileged path. ## Security Checklist – Reentrancy-exposed entrypoints use `nonReentrant` – CEI ordering is respected – Share math does not depend on raw `balanceOf(address(this))` – ERC-20 transfers use `SafeERC20` – Deposits measure actual tokens received – Oracle reads use TWAP or another manipulation-resistant source – Swaps require `amountOutMin` and `deadline` – Overflow-sensitive reserve math uses safe primitives like `mulDiv` – Admin functions are access-controlled – Emergency pause exists and is tested – Static analysis and fuzzing are run before production ## Audit Tools “`bash pip install slither-analyzer slither . –exclude-dependencies echidna-test . –contract YourAMM –config echidna.yaml forge test –fuzz-runs 10000 “`