Find More Bugs by Learning to Break a Protocol's Assumptions
Auditors talk a lot about invariants.
An invariant is something that must always hold true about a system. A vault's total assets, for example, should always cover the shares users can redeem.
Invariants sit on top of assumptions. An assumption is something the protocol believes about the world or its own components:
- ›Prices reflect reality
- ›Tokens behave like standard ERC20s
- ›Users cannot manipulate both sides of a calculation in one transaction
- ›Interest rates stay within expected ranges
I touched on this idea in a shorter post a while back, but it's one of those concepts that keeps proving itself during reviews. The more code you read, the more you see assumptions quietly shaping everything.

When an assumption fails, the invariant built on top of it can fail too. That gap is where many exploits live.
In the Saga bridge incident, the system assumed that any cross-chain message reaching its bridge logic represented a real deposit. That belief was never enforced as an invariant. An attacker simply supplied messages that looked valid but represented nothing, and the protocol minted value against fiction.

Assumptions often hide in:
- ›External integrations
- ›Math and accounting logic
- ›State transitions
- ›Multi-step user flows
Protocols trust oracles, bridges, tokens, and other contracts to behave "normally." Accounting logic often relies on values staying within ranges or not changing too fast. Multi-step flows like deposit → mint or borrow → repay rely on beliefs such as "this value cannot change between these steps" or "this function cannot be called in this order."
While reviewing a function, ask: what must be true for this logic to be safe? Not what the code checks — what it silently depends on.
If a lending protocol uses a price feed and allows borrowing up to 75% of collateral value, ask:
- ›What must be true about that price?
- ›Can it be manipulated?
- ›Can it move in the same transaction as the borrow?
- ›What happens if liquidity is thin or delayed?
List the assumptions first. Then try to make them false.
Once you write an assumption down, the next step is simple: how could this be false?
If the system assumes token transfers always move the exact amount requested, think about:
- ›Fee-on-transfer tokens
- ›Rebasing tokens
- ›Tokens that execute code during transfers
If it assumes users cannot affect a value mid-execution, consider:
- ›Flash loans
- ›Reentrancy
- ›Calling another function that mutates the same storage
This turns vague "edge cases" into concrete attack paths.
AI can help at the brainstorming stage. After identifying sensitive logic, you can ask:
- ›What assumptions does this logic rely on?
- ›How might those assumptions fail in practice?
- ›How could an attacker influence these inputs within one transaction?
The goal here is idea generation. You must still verify each idea against the code and the protocol design.
A good exercise is to read real exploit postmortems or audit reports and work backwards:
- ›Identify the invariant that was broken
- ›Ask what assumption had to fail for that invariant to break
Over time, you will start seeing exploits as assumption failures rather than random bugs.
This habit trains you to look past what the code does and focus on what the system believes. When you can spot and challenge those beliefs, you start finding the bugs others read past.
If you want structured practice with this style of thinking, some of the security-focused courses on Cyfrin Updraft walk through real codebases in a way that makes you constantly ask what the system is assuming at each step.
