The pool is a constant-product AMM: reserves X and Y always satisfy x·y = k. A swap of size Δin (after a 0.3% fee) returns:
out = (Δin·(1-fee)·R_out) / (R_in + Δin·(1-fee))
A flash loan lets the attacker borrow the whole swap amount with zero collateral, as long as it is repaid — with a small fee — inside the same transaction:
- Swap in — the attacker dumps the flash-loaned Y into the pool, buying X. Y reserve rises, X reserve falls, so the spot price P = R_y / R_x spikes.
- Borrow — a lending protocol values the attacker's pre-existing X collateral using its price oracle. If that oracle just reads the pool's current spot price, it reads the spiked price and lets the attacker borrow far more Y than the collateral is really worth.
- Swap back & repay — the attacker sells the X back for Y (recovering ≈ the loan, minus two rounds of swap fee), repays the flash loan plus its fee, and walks away with the extra Y as profit — all inside one atomic block.
The TWAP defense: a time-weighted-average-price oracle averages the spot price over the last N blocks. One manipulated block only shifts that average by roughly:
P_used ≈ P_before + (P_spiked − P_before) / N
so a wide TWAP window dilutes a single-block spike almost to nothing — the attacker still pays the swap fees to move the price but the oracle barely notices, and net profit turns negative. This is exactly the defense real lending protocols (Compound, Aave-style oracle feeds) adopted after early flash-loan exploits like the 2020 bZx incidents.
Fixed in this model: attacker collateral = 10,000 X, collateral factor = 75%, swap fee = 0.3% per leg, flash-loan fee = 0.09%.