Postern
The contract executed

Run on the chain, then deliberately broken

There is no testnet here, no toolchain, no funded account and no private key. An eth_call with no to executes creation code on the real EVM of Robinhood Chain and returns whatever the constructor returns, so each property below is a real execution against the real evaluator, costs nothing, and leaves nothing behind. Last run at block 56,798,152.

24/24
properties executed on chain 4663
12/13
sabotages caught
11 by a property, 1 by the ABI surface alone
41
public functions, checked against a written-down surface
compared to the compiled ABI, not to a grep of the source
8,406
bytes of creation code
nothing deployed — deploying costs gas from a wallet and this repository holds no key
The properties

All 24, and what each one does

#PropertyResult
P1A first deposit mints against its own assetsexecuted · pass
P2The ERC-4626 inflation attack does not workexecuted · pass
P3A donation does not move the share priceexecuted · pass
P4The protocol cut cannot exceed the hard capexecuted · pass
P5sweep() cannot reach principalexecuted · pass
P6openPostern refuses an unlimited allowance, as unlimitedexecuted · pass
P7openPostern refuses an over-large allowance, as inexactexecuted · pass
P8openPostern refuses an under-size allowance, as inexactexecuted · pass
P9After openPostern, nothing is left standingexecuted · pass
P10After the ordinary deposit, everything approved is still standingexecuted · pass
P11residual() predicts the real post-stateexecuted · pass
P12exposure() is the lesser of allowance and balanceexecuted · pass
P13A token that does not decrement is refused, and nothing is keptexecuted · pass
P14A fee-on-transfer token is credited what arrived, not what was askedexecuted · pass
P15A reentrant deposit cannot credit itself twiceexecuted · pass
P16previewDeposit agrees with depositexecuted · pass
P17previewRedeem agrees with redeemexecuted · pass
P18A round trip never returns more than went inexecuted · pass
P19Redeeming somebody else’s shares needs their allowanceexecuted · pass
P20Transferring the share transfers the claimexecuted · pass
P21harvest takes its cut from the gain, never principalexecuted · pass
P22harvest cannot book income the vault does not holdexecuted · pass
P23Both doors price the same deposit the same wayexecuted · pass
P24Only the front door leaves a key behindexecuted · pass
The sabotages

A suite nobody has shown to fail is a suite nobody has tested

Each row is the contract recompiled with one deliberate defect and run again. The defect is applied to a string in memory, never to a file, so an interrupted run cannot leave a broken Postern.sol in the repository — and every anchor carries the number of times it must match, so a search-and-replace that silently stopped matching throws instead of quietly testing nothing.

DefectWhat it doesCaught by
donation-pricedread assets from balanceOf instead of the storage counterP3
no-offsetremove the virtual share offset from both conversionsnothing — expected
cap-removedlet the owner set the protocol cut above the hard capP4
sweep-principallet the owner sweep the whole balance, principal includedP5
no-share-allowancelet anyone redeem anyone else’s sharesP19
price-after-assetsprice a deposit after its own assets have landedP2, P5, P16, P21
credit-requestedcredit what was requested rather than what arrivedP14
no-mutexremove the reentrancy guard the arrived-balance measurement requiresP15
unlimited-acceptedlet openPostern take an unlimited allowanceP6
inexact-acceptedlet openPostern take more allowance than the deposit needsP7
no-readbacktrust the token to have consumed the allowance instead of reading it backP13
residual-lieshave residual() always answer zeroP11
extra-owner-hatchadd an owner-only rescue() that no property forbidsthe ABI surface (rescue)
Some survive, and the reasons are results. Removing the virtual share offset breaks no property here, because the attack it defends against is already closed by tracked accounting: the attacker's donation never reaches the storage counter, so it never touches what the next depositor is quoted. Two locks on one door, and the offset is the optional one — which is not how it is usually sold. Adding an owner-only rescue() breaks no invariant any property states, and is seen only by a mechanical read of the compiled ABI. If nothing in this list were expected to survive, "caught by everything" would read as strength rather than as a suite whose edges nobody has probed.

Three of these were missed on the first run, and the reasons are worth having. Two guard properties only asked whether openPostern reverted — so removing the unlimited-allowance check changed nothing they could see, because the very next line still said no, for a different reason. They check the revert selector now. The third was worse: the reentrancy exploit never fired at all. The hook re-entered the vault as the token, the token had approved nothing, and the inner deposit died on allowance before it could do any damage — so the property passed against a vault with its guard removed. An attack that never fires is not a passing test, and the property now fails unless the hook records that it ran.

This is a self-review and not an audit. not audited by anybody; the sabotage suite is a self-review and says so. Nothing above tells you the contract is safe; it tells you exactly which claims have been executed and which defects this particular suite can see.
Deploy one from your wallet → How the two doors differ