Ten readings.
One finding.
A single detector that reports everything it sees is noise. Ten of them are ten times the noise. Quorum pairs its lenses by risk, two readings each for three of them and four for the access risk, and every lens reasons from different evidence.
quorum run
Agreement is signal.$ quorum run --targets fixtures/VulnerableVault.sol fixtures/OpenFeeSetter.sol QUORUM VulnerableVault.sol:withdraw reentrancy corroborated by callorder-lens, guard-lens QUORUM OpenFeeSetter.sol:setFeeRate unguarded-state-write corroborated by modifier-lens, sender-lens scanned 20 lens-units | confirmed 2 | recalled 1 | candidates 5
What a lens may not do
No lens can publish a finding on its own. A lens records what it saw and reads nothing about what its peers saw; the tally of who agreed lives on the finding in memory, not inside any agent. A second lens that reaches the same (contract, function, risk) from a different reading is what turns a sighting into a finding.
The lenses are deliberately simple. They are regex-and-brace-matching heuristics over source text, not a compiler front end. The point of this project is the coordination and memory layer; the lenses are the honest minimum needed to have something real to coordinate about.
callorder-lens
reentrancyAn external call precedes a state write in the same function.
- Evidence
- call ordering
- Pair
- guard-lens
guard-lens
reentrancyThe function moves value out and carries no reentrancy guard.
- Evidence
- guard absence
- Pair
- callorder-lens
On audited production code
Run Quorum against audited production contracts and it mostly holds its tongue. On Aerodrome’s Router, WETH9 and a Compound proxy it confirms nothing and files twenty candidates. That is the intended behaviour, not a failure to find bugs.
Disagreement is kept as a candidate and never published.
modifier-lens
unguarded-state-writeExternally callable, writes storage, carries no modifier at all.
- Evidence
- declaration
- Pair
- sender-lens
sender-lens
unguarded-state-writeWrites a privileged-looking variable with no msg.sender check anywhere on the path.
- Evidence
- reachability
- Pair
- modifier-lens
Not a vulnerability scanner that proves exploits
Quorum publishes corroborated idioms worth review, not confirmed vulnerabilities. A quorum means two independent lenses agreed on a shape, nothing more. The Friend.tech recall on the front page is a pattern match on a call idiom, not an allegation about that contract.
The fixtures in fixtures/ are vulnerable on purpose and are not deployed anywhere.
consistency-lens
unguarded-state-writeWrites state that the contract's other writers of it only write behind a guard, and carries no guard at all.
- Evidence
- the contract's own intent
- Pair
- modifier-lens
Why the access risk has four
The first two readings of this risk both key on how a function is written: whether it carries a modifier, and whether the variable it writes carries a privileged-looking name. Modern code writes last_gauge and _validatorsMap, so on recent audit findings the pair corroborated nothing at all. The third reading asks the contract instead: every other function that writes this variable carries a guard, and this one carries none. The fourth came from real 2026 hacks: a function anyone can call hands the caller’s own bytes to another contract, as itself. Two access bugs had been published as reentrancy because nothing else agreed with modifier-lens.
The third was built against bench/MODERN.md and the fourth against bench/HACKS-RECALL.md, and both files say so: neither recall there is a measurement on unseen code.
wrap-lens
unsafe-mathThe compiler lets this storage arithmetic wrap: a pre-0.8 pragma, or an unchecked block.
- Evidence
- compiler context
- Pair
- bound-lens
bound-lens
unsafe-mathNothing in the function bounds the operands before the write.
- Evidence
- guard absence
- Pair
- wrap-lens
Where the count lives
Corroboration is accumulated across lenses, processes and sessions on the finding entity in the WARM tier. A lens has no idea who else agreed with it; memory does.
The memoryledger-lens
accounting-mismatchThis balance has no way down anywhere in the contract, and this function relies on it.
- Evidence
- contract-wide
- Pair
- payout-lens
payout-lens
accounting-mismatchValue leaves this function against a balance it reads and never reduces.
- Evidence
- outflow
- Pair
- ledger-lens
What the accounting pair cannot see
It sees a balance that only ever grows while value goes out against it: a contract that adds to a balance in one function and never takes it back in another. It cannot see a sibling function that forgot one debit another function has, because there the balance does go down, on the other path. That shape needs a reader that follows calls.
Measured before it was posted: bench/ACCOUNTING.md.