Six common ways a Solidity contract loses money while every individual line looks correct. Each one is a pair: the vulnerable contract, and the same contract fixed. Each comes with a Foundry test that carries out the exploit against the vulnerable version and shows the fix stops it — no hand-waving, the theft either happens on-chain in the test or it doesn't.
The theme is bugs that don't revert. The deposit succeeds, the signature checks out, the arithmetic is individually right — and the funds are gone anyway.
| # | Class | The bug | Test |
|---|---|---|---|
| 1 | Vault inflation | First depositor inflates the share price with a donation; a later deposit rounds down to zero shares and is stolen | InflationAttack.t.sol |
| 2 | Reentrancy | withdraw sends ETH before zeroing the balance, so the callee re-enters and drains the bank |
Reentrancy.t.sol |
| 3 | Access control | setOwner has no guard; anyone points ownership at themselves, then withdraws |
AccessControl.t.sol |
| 4 | Oracle manipulation | Collateral is priced off a DEX pair's spot reserves; a one-shot swap pumps it and over-borrows | OracleManipulation.t.sol |
| 5 | Unchecked return | A token returns false instead of reverting; the vault ignores it and credits a phantom deposit |
UncheckedTransfer.t.sol |
| 6 | Signature replay | A signed claim has no nonce, so one signature is replayed until the contract is empty | SignatureReplay.t.sol |
Each fix is the standard mitigation: virtual shares (OZ ERC4626), checks-effects -interactions plus a guard, a gated two-step ownership transfer, a TWAP instead of spot, a checked return value, and a single-use signature digest.
forge test # 12 tests: two per finding (exploit + fix)
forge test -vv # with the logged amounts (e.g. borrow limit after manipulation)
They are the recurring high- and medium-severity findings in real audit contests. The point of the repo is the method, not novelty: for every defect, write the test that reproduces it before writing the fix, and keep the exploit in the suite so a regression can't quietly bring it back.