Cupel
A vault tells you what your shares are worth. That is not the same as what the vault will let you take, and the standard says the second number is a promise. On this chain, 15 of 43 live vaults will not keep it.
What the standard actually says
A quote is not advice.
It is a claim about the next transaction.
EIP-4626 is unusually blunt about one function:
maxWithdraw— “MUST return the maximum amount of assets that could be transferred from owner throughwithdrawand not cause a revert.”
So it is falsifiable. Ask a vault what you can take, then ask it to give you
exactly that, and see which answer it stands behind. No fork, no testnet, no key:
eth_call runs the vault's own bytecode against its own storage at head, and costs nothing.
49 vaults in the census; 43 could be measured at head. 4 have no shares outstanding and 2 had no holder inside the scanned window — both are excluded and counted rather than folded in. Every row is published.
The number a holder feels
The quote is the last thing you check, and the first thing that fails.
maxWithdraw returns zero for their own largest holderThe largest position quoted nothing
In STATE, the largest holder this sweep reached owns shares the vault
itself values at 71,348,022 DEEP. maxWithdraw for that address returns
zero.
The vault is holding 0.0% of what it says it is worth.
0xbfb7b3ff3d498a559b946b836d26f0e168f273d5
Why it is called Cupel
A claim about metal is settled by fire,
not by the seller.
A cupel is a small porous cup of bone ash. The sample goes into it molten, and everything that is not the noble metal is drawn out into the walls of the cup. What is left sitting in the bottom is a bead of the real thing, and it is weighed. That number is the number — and it is allowed to disagree with the one on the invoice.
A vault's balance sheet is the invoice. It is an honest statement of what the vault owns, and it is
not a statement about what you can have. maxWithdraw is the assay: EIP-4626 defines it as
the amount withdraw will actually hand over, which means it can be put in the fire.
So that is what this does. Every vault on the chain is quoted, then asked for exactly its own quote, and the answer that counts is the one the EVM gives.
How Cupel answers it
Two bounds, and the exit path reads the same function that published them.
The inversion
maxWithdraw is not convertToAssets(balanceOf(o)). It is the largest
a for which previewWithdraw(a) still fits in your balance — solved exactly,
through the same roundings withdraw will perform.
With an exit fee there are two nested ceilings between assets and shares. Converting straight through them lands one unit high, and one unit high is a revert.
The liquid bound
A vault that has deployed its assets is worth more than it can pay. liquid() is what can
be paid this block, and a holder's claim on it is pro rata — not first come.
So a shortfall is shared in proportion. Whoever arrives first cannot take the liquid down to nothing and leave everybody behind them holding a quote the vault can no longer honour.
Executed, not asserted
26 properties and 20 sabotages, run on this chain's own EVM.
The properties run for real
All 26 run through eth_call with no to — real creation code on the real
evaluator, against real chain state. 26 of 26 hold.
And each guard is broken on purpose
The contract is compiled again 20 times, each with one defect, and every defect must be caught by a specific property. 20 of 20 behave as declared.
Including the ones that survive
1 sabotage survives and is declared, with the reason. Publishing only the caught ones hides where the floor is.
Ask it what you can take.
The app reads maxWithdraw and liquidFor straight from the
contract, and builds the exact calldata it would send. No backend, no API key, no private key.