Skip to content

0xan0nxyz/mini-amm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MiniAMM

A minimal constant-product AMM in ~180 lines of Solidity, built with a security-first mindset.

Every function documents its invariants. Every invariant is tested — both with property-based fuzzing (1000 runs) and stateful invariant fuzzing (500 sequences × 50 calls = 25,000 calls per invariant).

Invariants

ID Invariant Tested By
I-1 k never decreases after a swap (fees only grow k) testFuzz_swap_kNeverDecreases, invariant_kNeverDecreases
I-2 Both reserves > 0 while LP shares exist invariant_reservesNonZeroWhileSharesExist
I-3 LP shares proportional to liquidity contributed testFuzz_addRemove_noMoreThanDeposited
I-4 MINIMUM_LIQUIDITY permanently locked (first-deposit guard) invariant_minimumLiquidityPermanentlyLocked
I-5 Full token accounting: reserves == total_in - total_out invariant_tokenAccounting
I-6 Tracked reserves match actual token balances invariant_reservesMatchBalances

Known Attack Vectors & Mitigations

Attack Mitigation
Price manipulation via donation Reserves tracked internally, not via balanceOf
First-depositor ratio grief MINIMUM_LIQUIDITY burned to address(1) on first deposit
Reentrancy on swap Checks-Effects-Interactions ordering throughout
Slippage / sandwich minAmountOut parameter on swap()
Integer overflow in k Solidity 0.8 checked math

Test Results

Ran 19 tests: 19 passed, 0 failed

Unit + Fuzz (1000 runs each):
  [PASS] test_addLiquidity_firstDeposit
  [PASS] test_addLiquidity_subsequentDeposit_proportional
  [PASS] test_addLiquidity_reverts_zeroAmount
  [PASS] test_removeLiquidity_returnsProportionalTokens
  [PASS] test_removeLiquidity_reverts_insufficientShares
  [PASS] test_swap_basicToken0In
  [PASS] test_swap_reverts_slippage
  [PASS] test_swap_reverts_invalidToken
  [PASS] test_minimumLiquidityLocked
  [PASS] testFuzz_swap_kNeverDecreases          (1001 runs)
  [PASS] testFuzz_swap_token1In_kNeverDecreases (1001 runs)
  [PASS] testFuzz_addRemove_noMoreThanDeposited (1000 runs)
  [PASS] testFuzz_swap_priceImpactMonotonic     (1001 runs)

Stateful Invariant (500 sequences × 50 calls = 25,000 calls each):
  [PASS] invariant_kNeverDecreases
  [PASS] invariant_reservesNonZeroWhileSharesExist
  [PASS] invariant_minimumLiquidityPermanentlyLocked
  [PASS] invariant_totalSharesFloor
  [PASS] invariant_tokenAccounting
  [PASS] invariant_reservesMatchBalances

Note: During development, invariant_tokenAccounting caught a wrong assumption in the ghost variable design — a swap flowing token1 INTO the pool lets LPs withdraw MORE token1 than they deposited (fee accrual). The invariant was corrected from a naive withdrawn <= deposited check to a full accounting equation reserves == total_in - total_out, which is the correct solvency property.

Architecture

src/
  MiniAMM.sol              constant-product AMM, NatSpec invariants on every fn
test/
  MiniAMM.t.sol            unit + property-based fuzz tests
  MiniAMM.invariant.t.sol  stateful invariant tests with Handler + ghost variables
script/
  Deploy.s.sol             deployment script

Run

forge test                        # all tests
forge test -vvv                   # verbose output
forge test --match-test invariant # invariant tests only
forge coverage                    # coverage report

Tech

  • Solidity 0.8.24
  • Foundry — forge test, fuzz, invariant
  • No external dependencies (no OpenZeppelin)

About

Minimal constant-product AMM in Solidity — documented invariants, Foundry fuzz + stateful invariant tests

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors