T29 · Protocol Security
What can an attacker accomplish with money alone and no code vulnerability?
- Capabilities practiced
- BuilderFinancial LiteracySystem Thinking
- Hands on
- Calculate the cost of attacking your protocol: funds, gas, time, and potential profit.
- AI Lab
- Have AI act as an attacker and draft attack plans. Rank them by cost and payoff, then propose defenses.
A Real-World Problem
Your protocol is live. Its contracts passed every T28 check: no reentrancy, clear permission boundaries, correct rounding direction, and timelocked upgrades. The audit report is clean, with no high-severity findings.
A month later, one afternoon, the protocol loses a large sum in minutes.
You inspect the transactions and become more confused:
- No function was called in an unexpected way
- No permission boundary was crossed
- Every line behaved exactly as written
- Each individual transaction was completely validIn plain language, the attacker borrowed a huge sum, used it to push an exchange price to an absurd level, then traded with your protocol at that price entirely according to its rules. They pocketed the difference, repaid the loan, and finished within one transaction.
They broke into nothing. They simply worked out that the numbers favored them and acted on it.
None of T28's defenses—CEI, access control, invariants—fired, because those defenses stop code from doing what it should not do. Here, the code did exactly what it was designed to do.
So this chapter asks a different question:
How do you defend when the attacker needs no vulnerability, only a profitable calculation?
Thought Experiment
Leave code aside and picture a casino.
The rules at one table are public, fair, and followed perfectly by the house. One game sets its odds from the direction of chips wagered over the last hour. More bets on one side shift the odds.
The rule seems fine until someone arrives with a cart full of chips.
In one minute, they wager half the cart heavily on one side. The table follows its rules and moves the odds to an extreme. Then they wager the other half in the opposite direction at odds they created themselves.
At settlement, they win much more than they spent moving the odds. They pack up and leave.
The casino was not tricked into breaking a rule. Every bet was valid. The failure lay in an unstated assumption:
It assumed the direction of incoming chips represented many independent judgments, not one person's capital.
That assumption holds on most days, because few people control that many chips. It fails as soon as one person can bring enough money at once.
Now replace the casino with your protocol and the cart of chips with money that can be borrowed for one transaction and repaid immediately at almost no cost. Two things become clear:
First, your protocol also hides an assumption that “normally nobody has that much money.” Perhaps nobody can buy out a pool at once, instantly acquire a voting majority, or move a price that far in one block.
Second, crypto turns “obtain enough money at once” from “almost impossible” into “one transaction.”
The thought experiment reduces to a sentence:
Find every “normally nobody can do this” assumption in your protocol. Then assume someone has unlimited money right now and ask again: can they do it?
Your Decision
Your protocol needs a price. Where do you obtain it? This choice largely determines whether the attacker can make the numbers work.
What Happens
Compare the four price methods, focusing on the cost of breaking each one:
| Approximate attack cost | Can that capital be borrowed? | Main side effect | |
|---|---|---|---|
| Instant pool price | Capital to push the pool to the target price | Yes, flash loan | Cheapest and most commonly exploited |
| Time-weighted | Cost of sustaining the deviation | Partly | Lag; fails during violent markets |
| Multi-source median | Manipulating a majority of independent sources | Depends on independence | Redundancy may be fake |
| Bonded challenge | Cost of defeating the challenge mechanism | Usually no | Slow; relies on active challengers |
The second and third columns give the first conclusion:
A protocol is safe only when the cost to exploit it exceeds the benefit. In crypto, the capital component of that cost can often be borrowed.
This changes the safety calculation. In T28, attackers were limited by their keys and reachable code paths. Here, the constraint is whether the numbers work. The principal field in that calculation can be enormous: within one transaction, a flash loan makes “owning a hundred million” and “owning a hundred” functionally similar.
The second conclusion is the chapter's core action:
To judge a protocol's safety, put yourself in the attacker's position and calculate the trade, rather than searching only for code vulnerabilities.
The formula is simple; filling its terms correctly is hard:
Attack proceeds - attack costs = attacker's net profit
Where:
Attack costs = capital cost + Gas cost + time cost + risk cost
Capital cost = flash-loan fee if a flash loan works (very small)
= opportunity cost of holding the capital otherwiseIf the difference is positive, the attack will happen—not merely might happen. Onchain systems are public; people everywhere calculate the same trade, and you only need to miss it once.
Build a Model
Attack-cost checklist: fill every field correctly
This is the chapter's main tool. For any attack idea, fill the table and check the sign of net profit.
| Cost | How to estimate it | Easy mistake |
|---|---|---|
| Capital | Flash-loan fee if possible; opportunity cost if not | Assume it can be borrowed unless a step must cross a transaction boundary |
| Gas | Transaction complexity times the prevailing Gas price | A complex attack may hit the block Gas limit |
| Time | Number of blocks and transactions needed | Crossing blocks changes capital cost from fee to opportunity cost |
| Risk | Chance of failure, front-running, or reverse arbitrage | One atomic transaction has near-zero execution risk; multi-step risk rises sharply |
Then fill in proceeds:
| Proceeds | How to estimate them |
|---|---|
| Assets withdrawable directly | The maximum funds this path can reach |
| Arbitrage from distorted prices | Price movement times the position size you can build |
| Collateral bought cheaply | Amount incorrectly liquidated or priced |
The most important decision is whether the capital can be borrowed. It can turn a cost of millions in locked-up principal into a small fee, a difference of several orders of magnitude.
There is one test:
If the whole attack fits in one transaction and the borrowed assets can be returned at its end, it can use a flash loan.
If so, assume the attacker can access the sum of what all lending protocols on the chain can lend at that moment. That is a lower bound, not an exaggeration.
Flash loans: erase the principal line
The mechanism matters because it amplifies this entire chapter:
Within one transaction:
1. Borrow a large amount without collateral
2. Use it for anything
3. Repay principal plus fee before the transaction ends
If step 3 fails, the entire transaction reverts as though steps 1 and 2 never happenedThe lender is safe: nonpayment rolls everything back. The consequence for protocol security is profound:
Flash loans create no new attack. They lower the threshold for every attack that “needs a lot of money” from owning that money to making the economics work.
A flash loan itself is not a vulnerability; the economic assumptions it amplifies are. The answer is not to “ban flash-loan users”—that is neither feasible nor useful. Build a protocol that never relies on an attacker lacking enough capital.
A direct implication you can turn into a code rule:
Wherever a price, share count, or weight is changed and then used in the same transaction, assume the attacker changed it with borrowed funds.
This should sound familiar: it is T28's “state updated after use” at the economic level. There it was reentrancy; here it is manipulation. The same pattern appears at different layers.
Four economic attack classes, calculated one by one
- Borrow capital
- Create a distorted state
- Trade with the protocol against that state
- Restore the state
- Repay and keep the difference
First: oracle manipulation. The distorted state is a price.
Proceeds = assets extracted from the protocol at the distorted price
Cost = flash-loan fee + Gas ≈ very small
Safety condition = cost to move the price to the target > proceeds availableDefenses combine the pricing approaches from “Your Decision.” Raise the price-movement cost with deep liquidity, time-weighting, and genuinely independent sources.
Second: governance attack. The distorted state is voting power.
Proceeds = funds a malicious proposal can transfer
Cost = cost of borrowing a voting majority + cost of waiting through a timelock
Safety condition = borrowing votes + waiting costs more than the proposal can takeThe key variables are whether votes can be borrowed and whether execution is timelocked. If governance tokens can be flash-loaned and votes execute immediately, an attacker can borrow votes, pass and execute a proposal, and repay in one transaction. Two controls matter: snapshot voting power before the proposal so newly borrowed votes do not count, and require a timelock before critical proposals execute so the community can react. This is the technical answer to Chapter 24's question, “Can one vote transfer the treasury?”
Third: liquidity attack. The distorted state is a pool's composition.
Proceeds = value extracted from the distorted pool or collateral bought cheaply after a bad liquidation
Cost = temporary control of large capital + Gas
Safety condition = reverse cost of restoring the pool > value extractedThe first-depositor problem in T28 is a special case. More generally, capital temporarily shifts price or weight in a thin pool and triggers protocol logic that depends on it.
Fourth: incentive attack. The distorted state is the contribution the protocol believes it saw.
Proceeds = subsidies, rewards, or airdrops claimed
Cost = creating fake contributions (often tiny)
Safety condition = cost to fake one contribution > reward for one contributionThis class may not look like an attack, but it is pervasive: wash trading for fee rebates, self-trading to climb rankings, and creating ten thousand addresses for an airdrop (a Sybil attack). Its arithmetic is simple: if pretending to be a valid user costs less than that user's reward, someone will pretend at scale. The drained paymaster in T27 belongs to this class.
A complete cost calculation
Apply the method to the opening scenario. Suppose your protocol values collateral using a pool's instantaneous price.
Attack idea:
1. Flash-borrow a large amount of base asset
2. Push the collateral price up in that pool
3. Borrow far more than the collateral's real value from your protocol
4. Move the pool price back (recovering some capital naturally)
5. Repay the flash loan and keep the excess borrowed assets
Calculate costs:
Capital = Can this fit in one transaction? Yes → tiny flash-loan fee
Gas = One complex transaction, material but far below proceeds
Time = One atomic transaction; no cost of holding capital across blocks
Risk = The transaction either succeeds or fully reverts; risk ≈ 0
Calculate proceeds:
= (inflated collateral price - true collateral price) × borrowable amount
= positive, often much greater than the costs above
Conclusion: positive net profit → this attack will occurNow change one defense and recalculate: replace the instantaneous price with a sufficiently long time-weighted price.
Step 2 changes: to lift the time-weighted price, the attacker must hold the
elevated price across multiple blocks.
Capital = flash loan no longer works (must cross blocks)
→ opportunity cost of a large principal
Time = multiple blocks; arbitrageurs trade against the deviation
Risk = rises from ≈ 0 to real risk of arbitrage and front-running
Conclusion: cost jumps from “fee” to “large principal + time + risk,”
often above proceeds → attack no longer paysNo code vulnerability was fixed. You changed one cost term from “borrowable” to “not borrowable.” That is a core technique of protocol security: make the route expensive enough, rather than merely closing one entrance.
System view: inherit the risks of your dependencies
Your protocol rarely stands alone. It uses another pool for prices, accepts assets from a bridge, or deposits funds into another protocol to earn yield.
You inherit each dependency's economic assumptions. Economic attacks thrive at these seams:
- Is your collateral a wrapped asset from a bridge? Its real value depends on that bridge remaining sound (T26).
- Does your yield come from depositing users' funds into another protocol? Its economic assumptions become yours.
- Does liquidation depend on liquidators making a profit? During violent markets it may stop paying them to act (T22 treated liquidation speed as a safety boundary).
The last protocol-safety step is therefore a dependency graph. Ask the same question for every external node:
If its economic assumptions fail, what happens on my side, and can I isolate the damage?
That graph is the skeleton of the threat model in the next chapter.
What It Is Called
An attack that uses a protocol's economic assumptions for profit without exploiting a code flaw.
The test is not “does the code have a bug?” but “does the trade pay?” Defense means raising attack cost above attack proceeds.
An uncollateralized loan borrowed, used, and repaid within one transaction; failure to repay reverts everything.
It is an amplifier, not an attack itself. It lowers the threshold for attacks needing large capital from “own the money” to “make the economics work.” In any attack analysis, ask first whether that capital can be borrowed.
Temporarily move a price a protocol depends on, then trade with the protocol at that distorted price.
The cost is roughly the capital needed to move the source to the target price. If the source is an instantaneous pool price movable with a flash loan, the effective cost can be tiny. Raise the movement cost through deep liquidity, time-weighting, and independent sources.
Acquire enough voting power to pass a proposal transferring funds to yourself.
Two decisive variables are whether voting power can be borrowed and whether execution is timelocked. A snapshot before the proposal stops newly borrowed votes; a timelock allows time to react and exit. Without either, one vote may empty the treasury.
One person poses as many independent users to claim incentives paid per person or per independent contribution.
The calculation is simple: if creating one apparently valid user costs less than that user's reward, someone will do it at scale. It also defeats multiple-source and multiple-guardian designs: redundancy depends on genuine independence.
Set rewards and penalties so honest behavior is each participant's best choice.
Test it the same way as security: calculate from each participant's position whether dishonesty can pay more than honesty. If it can, the incentive has a vulnerability even if every line of code is correct.
Hands-on
Work only against your own protocol in your own fork environment. This Lab produces an accounting report, not a real-world attack. Attempts against live deployed protocols are outside the exercise.
The output is a table of each “normally nobody can do this” assumption, its attack cost, and its proceeds.
List every economic assumption in your protocol.
Read every function and record each hidden “this normally will not happen.” Common examples:
- Nobody can buy out or overwhelm this pool at once
- The price cannot move that far in one block
- Nobody can acquire a voting majority instantly
- The first deposit will not be 1 wei
- Liquidation will always pay liquidators enough to act
- Nobody will create accounts at scale merely to claim rewardsAfter each line, add “what if someone has unlimited money right now?” This is the Lab's foundation. Accurate calculations cannot rescue an incomplete list.
For each assumption, decide whether the capital can be borrowed.
Apply the test: can the whole attack complete in one transaction and repay the loan at the end?
If yes, put “flash-loan fee” for capital cost; it may be negligible. If no, identify the step that must cross blocks and use the opportunity cost of holding the principal.
This is the table's most important judgment because it sets the order of magnitude of cost.
Fill the other three costs.
Gas: estimate transaction complexity times a conservatively high Gas price
Time: count required blocks and transactions
Risk: atomic transaction ≈ 0; for multiple steps, estimate failure,
front-running, and reverse-arbitrage probabilitiesTry a transaction in your fork environment so Gas becomes a measurement instead of a guess.
Fill in the proceeds.
For each assumption, calculate what can be taken if it fails: withdrawable funds, arbitrage spread, or underpriced collateral.
Use an upper bound on proceeds. The attacker will size the position to the maximum you permit.
Calculate net profit and rank the paths.
Net profit = proceeds - costsSort assumptions from highest to lowest net profit. The top line is where your protocol is most likely to fail. Include it with every assignment from this stage onward.
For every positive-net-profit path, verify it on a fork.
Use only your own fork and your own contracts. Turn the attack idea into a test and see whether it actually produces positive profit.
As in T28, a profitable fork test is the evidence that a route truly exists. If the calculation says it pays but the test cannot make a profit, some entry in your calculation is wrong; find it.
Change one defensive variable for the highest-ranked route and recalculate.
Examples from this chapter: replace instant pricing with time-weighting, add governance snapshots and a timelock, add virtual offsets to shares, or make claiming incentives costly enough.
Recalculate until net profit changes from positive to negative. Rerun the fork test and confirm it no longer earns profit.
Put the before and after calculations side by side. That is the Lab's result: it proves you made the route expensive, not merely that you blocked one known exploit.
AI Lab
Let AI do what it does well—generate attack ideas—while you retain the work it handles poorly: precise calculation and ranking. Use three steps.
Step one:
Here is my protocol, including contracts, price sources, governance, rewards,
and external protocols it depends on.
Act as an attacker using capital but no code vulnerability.
List at least six ideas. For each, specify:
- What you borrow, what you do, and in what order
- Which of my economic assumptions you exploit
Step two:
For each idea, fill out capital (can a flash loan work?), Gas, time, risk,
and maximum proceeds. Explicitly decide whether the entire attack fits in
one transaction and repays the loan.
Step three:
Propose a defense for each. Distinguish a defense that blocks one specific
route from one that removes the economic assumption being exploited.
If it only blocks a route, explain which variable the attacker can change.Models often do well on step one because listing attack possibilities is a divergent task. They may find combinations you missed, which is their main value here.
Step two is where subtle errors appear: models often misclassify the capital cost. They may call an across-block attack flash-loanable, understating costs by orders of magnitude, or assume an atomic attack needs large owned capital, overstating cost and giving false comfort. Review this field yourself for every path. The test is whether everything can complete and repay in one transaction.
Step three separates weak from strong defenses. A model may suggest blocking one route, such as a price-deviation check in one function. An attacker can change the entry point. A stronger defense removes the economic assumption—use a time-weighted price so manipulating it becomes broadly expensive. Asking which variable bypasses each defense exposes the difference.
Finally, do two things yourself: rank paths by calculated net profit, not intuition, and calculate the top path before and after the defense. Those are the skills this chapter teaches and cannot be delegated to the model. It can suggest attacks; you decide which is most dangerous and whether the defense makes profit negative. The line is the same as T28: AI suggests leads; you establish conclusions.
Verify these points yourself after using AI
- For each attack, check its capital assumption: whether a flash loan works determines whether this field is right
- Check whether it incorrectly treats an attack that must cross blocks as flash-loanable, understating cost by orders of magnitude
- Check that proceeds use an upper bound; attackers maximize positions, so a typical case understates risk
- Try each proposed attack on your own fork; if it cannot earn profit, either the calculation is wrong or your defenses work
- Separate a defense that blocks one route from one that removes the economic assumption; an attacker can vary a route
- Check external dependencies and the economic assumptions inherited from them; the model often misses these
- Rank by net profit yourself and calculate the top route before and after your defense
Real-World Case
A lending protocol values collateral from an onchain pool's instant price. Someone flash-borrows substantial capital, raises the collateral's price in that pool, borrows assets far beyond the collateral's real value, moves the price back, and repays—all in one transaction.
On review, no line of code behaved unexpectedly and every transaction followed the rules. The hidden assumption was that “this pool price reflects the real market,” even though borrowed capital could move it.
This is the opening scenario and the reason for this chapter. The defense is not a code fix but a different price method that makes moving the price cost more than the amount extractable.
A protocol's governance token can be borrowed, and a passed proposal executes immediately without a timelock. Someone borrows enough tokens, votes for a proposal sending treasury funds to themselves, executes it, and returns the tokens.
Everything follows governance rules. The voter really holds a majority of (borrowed) votes, and the proposal really passes.
Two safeguards stop it: snapshot voting power before proposal creation (the borrowed votes were not held at snapshot time) and timelock execution (the community can notice and react). Chapter 24's question about one vote moving the treasury is precisely this.
T28 explained this from the code side. Economically, it is a liquidity attack: “the first deposit may be tiny” plus “total assets are read directly from the balance” lets a direct transfer lift share price until later deposits calculate to zero shares.
The cost is low—a small first deposit and a direct transfer. The proceeds are the next depositor's entire deposit. It follows the five-step pattern of fabricating a state, trading against it, and keeping the difference.
Use all three defenses: internal accounting, virtual shares, and dead shares, plus a revert if calculated shares are zero.
A protocol pays rebates based on trading volume to attract users. Someone scripts trades with themselves, creates large apparent volume, and empties the rebate pool. Their only costs are Gas and fees, which the rebates cover.
No vulnerability was exploited. The protocol did exactly what it promised: pay for volume. The problem is that volume can be faked at near-zero net cost.
The calculation is simple: if the net cost of fabricating one trade is lower than its rebate, someone will repeat it indefinitely. Check incentive design by running that calculation, not by reading code alone.
Change One Variable
The capital cost jumps from a flash-loan fee to large principal held through a deviation, plus opportunity cost. Risk jumps from near zero to actual exposure to arbitrageurs and front-runners while the deviation persists.
Many instant manipulations stop paying. But the attack is not eliminated: a truly illiquid asset can still have its average moved slowly with little capital. Lag can also delay necessary liquidations during volatile markets.
The conclusion is that one class of attack becomes expensive while another risk appears. No defense is free; move risk where you can better bear it.
Borrowed-vote attacks are mostly closed: attackers cannot temporarily assemble a majority, and a snapshot keeps even newly bought votes from counting immediately.
The cost is reduced flexibility and edge cases for legitimate large holders, delegation, and liquid staking. This also does not stop a patient attacker who holds a majority for a long time. Against that person, you still need a timelock and community attention.
Governance security is layered: borrowed-vote resistance, snapshots, timelocks, and community oversight each raise one part of the cost.
You changed none of your own code, but your attack-cost table may shift downward: attackers can borrow more, making some previously unaffordable attacks profitable.
This is a counterintuitive and crucial point: someone else's parameter change can reduce your security without notifying you.
Attack costs cannot be calculated once and forgotten. Every external variable—pool depth, borrowable amount, Gas price—moves. Recalculate periodically, especially when dependencies change. The next chapter turns this into ongoing monitoring.
Its capital changes immediately from “borrowable” to “not flash-loanable,” because a flash loan must be repaid in one transaction. The attacker needs substantial owned principal or a more expensive longer-term loan and faces detection, front-running, and reverse arbitrage in the interim.
Turning an atomic attack into a multi-step one is itself an effective defense. Timelocks, challenge periods, and staged execution all force an across-block holding cost onto the attacker, preventing the principal field from being nearly free.
The challenge period in T26, recovery timelock in T27, and governance timelock here apply the same idea in different contexts: trade delay for safety.
Takeaway Questions
Who bears the risk? In economic attacks, liquidity providers and depositors often share the loss. They did nothing wrong; they put money into a system whose attack economics worked. A protocol team must make each path's profit negative, rather than relax because an audit found no code flaws. No code vulnerability does not mean no economic vulnerability.
What are the incentives? This chapter turns that into a safety question: for every incentive you design, calculate whether dishonest behavior could earn more than honest behavior. If so, the incentive is an attack surface. Wash trading, Sybil accounts, and self-trading follow from bad incentive arithmetic.
If a token goes to zero, can the product still run? A sharper form here is: if an economic assumption behind a price, a pool, or an external protocol you rely on fails, what happens to yours? Ask this for every node in your dependency graph. The answers form your systemic-risk list and begin the next chapter's threat model.
Chapter Check
It is an equation: are attack proceeds minus attack costs positive? If so, the attack will happen, not merely might. Onchain systems are public; everyone can calculate the same trade, and you only need to miss it once.
The most important cost is capital, which can often be borrowed. The real question is not whether an attacker owns the money but whether borrowed money can complete the attack.
Defense means raising attack cost above proceeds, often by changing a cost from “borrowable” to “not borrowable.”
A flash loan is safe for the lender: failure to repay reverts the entire transaction. It does not create a new kind of attack.
It lowers the threshold for every capital-heavy attack from “own a lot of money” to “make the economics work.” If an attack can finish and repay within one transaction, assume the attacker can access all capital currently lendable across the chain.
So ask first of any attack: can its capital be borrowed? That answer can separate a small fee from millions in required principal.
Instant pool price: cost ≈ capital to move the pool to the target price. That capital can be flash-borrowed, leaving a fee as effective cost. This is the cheapest and most commonly attacked.
Time-weighting: the attacker must sustain a deviation for multiple blocks. Flash loans no longer work, so capital's opportunity cost and arbitrage risk rise sharply. Thinly traded assets may still be moved slowly.
Multi-source median: cost becomes manipulating a majority of sources, provided they are truly independent. If they share an underlying pool, redundancy is illusory.
These methods are often combined to raise the cost of moving the price.
Snapshot voting power before the proposal: stops newly borrowed votes. The snapshot predates the loan, so the attacker does not hold those votes at the relevant time.
Timelock before execution: stops immediate effect. Even if an attacker wins the vote, the community can see and respond by exiting, vetoing, or taking emergency action.
You need both. A patient large holder can bypass a snapshot alone; borrowed votes can pass the first gate if there is only a timelock. Together they make “one vote empties the treasury” much more costly.
There is no single answer. Check these points:
- Did you list every economic assumption, adding “what if someone has unlimited money right now?” to each?
- Did you decide whether each path can use a flash loan? The test is completion and repayment in one transaction.
- Are proceeds calculated as an upper bound? Typical-case proceeds understate what a maximum-sized attacker can take.
- Did you rank by net profit? The top path belongs in your assignment as the likely failure.
- Did the top path earn positive profit in a fork test? If not, find the wrong entry in your calculation.
- Does your defense remove the economic assumption or only block one route an attacker can vary?
- Did you include external dependencies, whose economic assumptions you inherit and whose parameters can change?
Points 6 and 7 separate a calculation from real understanding: make the route expensive, not just one entrance inaccessible, and recalculate as the surrounding system changes.
Key idea
Economic attacks need no software bug—only a profitable calculation.