A production-grade Uniswap V2-style Automated Market Maker with extensions including TWAP oracle, adjustable fees, and sandwich protection. Built with Foundry.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AMM SYSTEM OVERVIEW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββ
β AMMRouter β
β β
β β’ addLiquidity β
β β’ removeLiquidityβ
β β’ swap* β
β β’ getAmounts* β
βββββββββ¬βββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β AMMPair β β AMMPair β β AMMPair β
β (A/B) β β (B/C) β β (A/WETH) β
β β β β β β
β β’ LP tokens β β β’ LP tokens β β β’ LP tokens β
β β’ TWAP oracleβ β β’ TWAP oracleβ β β’ TWAP oracleβ
β β’ swap() β β β’ swap() β β β’ swap() β
β β’ mint() β β β’ mint() β β β’ mint() β
β β’ burn() β β β’ burn() β β β’ burn() β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β β
βββββββββββββββββββΌββββββββββββββββββ
β
βββββββββΌββββββββ
β AMMFactory β
β β
β β’ createPair β
β β’ getPair β
β β’ setFeeTo β
β β’ setSwapFee β
βββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SWAP FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User Router Pair Tokens
β β β β
β swapExact...() β β β
β βββββββββββββββββββΊβ β β
β β β β
β β transferFrom(user) β β
β β ββββββββββββββββββββββΌββββββββββββββββββββββ
β β β β
β β transfer(pair) β β
β β ββββββββββββββββββββββΌβββββββββββββββββββββΊβ
β β β β
β β swap(out, to) β β
β β βββββββββββββββββββββΊβ β
β β β β
β β β verify K invariant β
β β β update reserves β
β β β update TWAP β
β β β β
β β β transfer(user) β
β β β βββββββββββββββββββββΊ
β receive tokens β β β
β ββββββββββββββββββββΌβββββββββββββββββββββββΌββββββββββββββββββββββ
The core invariant of the AMM:
x Γ y = k
where:
x = reserve of token A
y = reserve of token B
k = constant (increases with fees)
For a swap of Ξx tokens in:
Ξy = (y Γ Ξx Γ (10000 - fee)) / (x Γ 10000 + Ξx Γ (10000 - fee))
where:
Ξx = input amount
Ξy = output amount
fee = swap fee in basis points (default: 30 = 0.3%)
Price Impact = 1 - (output_amount / (input_amount Γ spot_price))
Spot Price = reserve_out / reserve_in
First deposit:
LP_tokens = sqrt(amount0 Γ amount1) - MINIMUM_LIQUIDITY
Subsequent deposits:
LP_tokens = min(
(amount0 Γ totalSupply) / reserve0,
(amount1 Γ totalSupply) / reserve1
)
Time-Weighted Average Price calculation:
price0CumulativeLast += (reserve1 / reserve0) Γ timeElapsed
price1CumulativeLast += (reserve0 / reserve1) Γ timeElapsed
TWAP = (priceCumulative_end - priceCumulative_start) / timeElapsed
- Constant Product AMM: x Γ y = k invariant
- LP Tokens: ERC-20 liquidity provider tokens
- TWAP Oracle: Cumulative price tracking for external use
- Flash Loans: Borrow tokens within a single transaction
- Skim/Sync: Balance recovery utilities
- Pair Creation: CREATE2 for deterministic addresses
- Adjustable Fees: Configurable swap fee (max 1%)
- Protocol Fees: Optional fee recipient for protocol revenue
- Liquidity Management: Add/remove with slippage protection
- Token Swaps: Single and multi-hop swaps
- ETH Support: Native ETH via WETH wrapper
- Deadline Protection: Transaction expiration
# Clone the repository
git clone https://github.com/Kazopl/amm-uniswap-v2-plus.git
cd amm-uniswap-v2-plus
# Install dependencies
forge install OpenZeppelin/openzeppelin-contracts@v5.0.1
forge install foundry-rs/forge-std
# Build
forge build
# Run tests
forge testCreate a .env file:
PRIVATE_KEY=your_private_key_here
ARBITRUM_SEPOLIA_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
ARBISCAN_API_KEY=your_arbiscan_api_key
WETH_ADDRESS=0x... # WETH on Arbitrum Sepolia# Run all tests
forge test
# Run with verbosity
forge test -vvv
# Run unit tests
forge test --match-path "test/unit/*"
# Run fuzz tests
forge test --match-path "test/fuzz/*"
# Run invariant tests
forge test --match-path "test/invariant/*"
# Gas report
forge test --gas-reportsource .env
WETH_ADDRESS=0x... forge script script/DeployAll.s.sol:DeployAll \
--rpc-url $ARBITRUM_SEPOLIA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
--etherscan-api-key $ARBISCAN_API_KEY| Threat | Mitigation |
|---|---|
| Reentrancy | ReentrancyGuard on all state-changing functions |
| Price Manipulation | TWAP oracle, not spot price for external use |
| Sandwich Attacks | Slippage protection via amountMin parameters |
| Flash Loan Attacks | K invariant check after callback |
| Integer Overflow | Solidity 0.8.24 built-in checks |
| Front-Running | Deadline parameter, user-set slippage |
| LP Drain | Minimum liquidity permanently locked |
- MEV/Sandwich: Mitigated by slippage parameters
- Oracle Manipulation: TWAP smooths price over time
- Liquidity Removal: Proportional burning ensures fairness
- Flash Loans: K invariant must hold after callback
reserve0 Γ reserve1never decreases (K invariant)- LP total supply β₯ MINIMUM_LIQUIDITY
- Token balances β₯ reserves
- Swap fee β€ MAX_SWAP_FEE_BPS
| Function | Gas (approx) |
|---|---|
createPair() |
~2,500,000 |
addLiquidity() |
~150,000 |
removeLiquidity() |
~120,000 |
swapExactTokensForTokens() |
~100,000 |
mint() |
~120,000 |
swap() |
~80,000 |
amm-uniswap-v2-plus/
βββ src/
β βββ AMMPair.sol # Pair with LP tokens + TWAP
β βββ AMMFactory.sol # Pair factory
β βββ AMMRouter.sol # User-facing router
β βββ interfaces/
β β βββ IAMMPair.sol
β β βββ IAMMFactory.sol
β β βββ IAMMRouter.sol
β β βββ IWETH.sol
β βββ libraries/
β β βββ AMMLibrary.sol # Math helpers
β βββ mocks/
β βββ MockERC20.sol
β βββ WETH.sol
βββ test/
β βββ BaseTest.sol
β βββ unit/
β β βββ AMMFactory.t.sol
β β βββ AMMPair.t.sol
β β βββ AMMRouter.t.sol
β βββ fuzz/
β β βββ AMMFuzz.t.sol
β βββ invariant/
β βββ AMMInvariant.t.sol
βββ script/
β βββ DeployAll.s.sol
β βββ Interactions.s.sol
βββ foundry.toml
βββ README.md
MIT License - see LICENSE for details.
- Uniswap V2 - Original AMM design
- OpenZeppelin Contracts
- Foundry
- Cyfrin - DeFi course patterns