Hot potato pattern with accidental "drop":
The pattern relies on structs with zero abilities to force explicit consumption. But I keep seeing developers accidentally add "has drop":
struct FlashLoanReceipt has drop { // Bug: can now be ignored pool_id: ID, amount: u64, }
Now the receipt just gets auto-dropped and tokens aren't returned. Found this in production code right before deployment.
Missing phantom type parameters:
Receipts that don't bind to the coin type being used:
struct PaymentReceipt { // Missing: phantom CoinType amount: u64, }
Someone can pay with worthless ScamCoin, get a receipt, then claim real items. Adding "phantom CoinType" fixes this but it's easy to miss.
Option<T> with non-droppable types:
Even empty Options need explicit option::destroy_none() calls if they wrap non-droppable types. This catches people off guard.
Related: the Cetus incident ($220M, May 2025)
The bug wasn't in Cetus's code but in integer-mate, a math library dependency. Three audits checked ability annotations carefully but glossed over dependencies. Type safety prevented whole vulnerability classes but couldn't catch the bit-shift validation error.
Move's type system is genuinely better than alternatives, but adding "has drop" to a hot potato doesn't generate warnings - it just silently works. Same with "copy + drop" on tokens.
I've started treating ability annotations as the most security-critical part of the code, not just boilerplate.
What mistakes have you seen in Move development? Are there other patterns beyond abilities that trip people up? Would love to hear what's caught you or your teams.