- Check if works with fee/rebase token
- Check if uses safeTransfer (and if token without return true should be compatible, see (https://github.com/zeroknots/brokentoken)
- Check if token can exist or not before calling function (especially with solmate safetransfer)
- Check if transfer always from msg.sender or could transfer from someone else that approved
- Check if different token decimals are possible and handled
- Can contract end up requiring a 0 amount of ERC20 transfer which would revert for some ERC20
- safeApprove allows only to approve from zero
- Main tokens like weth or btc on xdai have a callback
- Check if reetrancy
- Check if using safeTransfer
- Check precision loss, always multiply before dividing
- Check overflow/underflow
- Check uint used in the calcul and convertions (1 day is uint24)
- Check if reetrancy (not only modifier but also balances update order and view function result change)
- Check if msg.value is used in a loop
- Use Call to transfer ETH
- Check if oracle heartbeat allow arbitrage/mev
- Check if oracle can be changed atomically
- Check that doesn't rely on spot price and uses at least twap or chainlink oracle
- Check if decimal price is correct (100$ could become 100k$)
- Check if oracle include a vault that can have value per share changed
- Read-only reentrancy for curve virtual price (on ETH pools)
- Check minOut is valid and doesn't open MEV/arbitrage opportuniy
- Check for dust on zap (https://blog.alphaventuredao.io/onesideduniswap/)
- https://dacian.me/defi-slippage-attacks
- Initialized and can't be initiliazed again (implementation & proxy)
- Check variable order and state shadowing (storage collision & function clashing (check signatures))
- DelegateCall to selfDestruct/unexpected code
- DelegateCall returns true if target does not exist
- Many OpenZeppelin upgradeable contracts need to be initialized in the importing contract's constructor.
- admin functions protected
- 2 step ownership change
- Multisig over EOA
- Protection against first deposit shares/donation attack (https://twitter.com/naddison/status/1640549868442902528)
- Doesn't assume share value being constant
- Pull Over Push -> user should be able to withdraw for themself
- Check return value (especially if address.call /!\ as could silently revert or not fully execute)
- Phantom function that might not revert
- Doesn't allow arbitrary call or if does make sure to not be msg.sender in these calls
- Assembly check on calldata/target could be bypassed (see abi-smuggling on damnVulnerableDefi)
- Time or nonce limit
- erecover can return 0 if invalid signature
- An address can create multiple different signatures for the same message (https://github.com/0xbok/ecdsa-vuln-poc/tree/master/ch2_not_unique)
- https://github.com/0xbok/ecdsa-vuln-poc
- Time between proposed and execution
- Flashloan attack governance
- Loop if iterate too many times
- One elements of the loop fail and makes the whole transaction fail (Asymmetry safeth)
- address.call that check for success on a contract that can consume more gas than expected
- Can a reorg (happens often on polygon or bnb) result in loss of funds
- If fork of a famous protocol check this list: https://github.com/YAcademy-Residents/defi-fork-bugs
- Token order might change for Uniswap Lps (token0 & token1) and all other contracts that determine variable order by address
- ZKsync era doesn't support address.transfer
- https://era.zksync.io/docs/dev/building-on-zksync/contracts/differences-with-ethereum.html#evm-instructions