Pre-Quoted. Not Safe.
Most people missed this. Here's exactly what was wrong.
Hey, it’s Arsen.
In today’s menu:
Full CTF answer — the reconstitute bug, completely broken down
Why off-chain quoting is a repeatable Medium finding pattern
And more…
🏴☠️ Hacks / Bounties
$50M Aave Swap — Extreme Price Impact (Link)
Venus Protocol — $2.18M Bad Debt (Link)
dTRINITY Flash Loan — $257K Drained (Link)
🗞️ News
📙 Education
Last Week’s CTF — Full Answer
Most replies mentioned slippage.
A few said stale pricing.
Almost nobody got the exact mechanism.
Here it is.
The contract runs two phases.
Phase 1: Sell every holding into base.
Simple. Approve the DEX, call the swap, measure the delta. baseReceived builds up across every holding.
Phase 2: Buy new assets using base.
for (uint256 j; j < newAssets.length;) {
Token(base).approve(dex, allocateAmounts[j]);
(bool ok,) = dex.call(buyData[j]);
require(ok);
unchecked { ++j; }
}
One question changes everything:
Where does allocateAmounts come from?
function reconstitute(
...
uint256[] calldata allocateAmounts
)
It’s an input param.
Passed in from an off-chain entity —
a bot, a governor script, some automation.
That off-chain entity did this:
Called a view function on the DEX.
Simulated how much base Phase 1 would produce.
Built allocateAmounts from that simulation.
Then triggered reconstitute.
The gap:
The view call happens before execution.
Actual execution happens later.
Prices move in between.
Price moves up — contract receives more base than expected.
Phase 2 completes. Leftover base sits unspent, unaccounted.
Price moves down — contract receives less base than expected.
Phase 2 tries to spend allocateAmounts.
Doesn’t have enough. Swap fails. Transaction reverts.
DoS.
That’s a Medium.
The question that catches it every time:
“Is the amount I’m spending in Phase 2 guaranteed to match what I received in Phase 1?”
If allocateAmounts comes from outside the contract —
no guarantee.
The fix:
Validate onchain before Phase 2 runs:
uint256 totalNeeded;
for (uint256 j; j < allocateAmounts.length;) {
totalNeeded += allocateAmounts[j];
unchecked { ++j; }
}
require(baseReceived >= totalNeeded, "insufficient base");Or better — derive allocateAmounts from baseReceived
directly inside the contract. Remove the off-chain dependency entirely.
The auditor pattern to carry forward:
When you see a value passed as calldata from an off-chain trigger — pause.
Ask three things:
Where was this calculated?
When was it calculated?
Can the state it referenced change before execution?
“Pre-quoting” gives the off-chain system confidence.
But confidence at quote time isn’t safety at execution time.
You’ll see this in:
Portfolio rebalancers (this one)
AMM-based yield strategies
Cross-chain transfers with off-chain routing
Any system where automation pre-computes amounts
and passes them in as params.
Every time — ask the same three questions.
Simple question. Consistent Medium. Sometimes High.
Arsen
Reply with what you want me to cover next — I read every one.

