Sections
Sections
- What this measures, and why it exists
- The headline, and it is our own defect
- The sampling rule, published in place of the table
- The two numbers: cold before repair, cold after repair
- The wall taxonomy W1 through W7, before and after
- What each wall costs to retire
- What the harness actually covers
- What the repairs did, and what they did not do
- What this census does not assert
- Limits, UNPROBEABLE items, and the provenance ledger
What this measures
1. What this measures, and why it exists
Uniswap's own v4 security guidance, section 7, Security Best Practices Checklist, under Accounting
safety, says this verbatim (read live 2026-08-01 from
developers.uniswap.org/docs/protocols/v4/security):
"Invariant & Integration Testing use invariant and stateful fuzz testing (e.g., with Foundry or Echidna) to check properties such as delta conservation, fee bounds, and balance consistency across full PoolManager flows."
That is a recommendation addressed to every hook author. Nobody appears to have measured whether it is actually achievable. This census is that measurement, on one specific and narrow reading of it: take a harness that exists, point it at hooks nobody involved chose, and count how many complete an invariant run.
The question is not "are these hooks good". The question is "can the tooling reach them at all", and the answer turns out to be dominated by properties of tooling and of packaging, not by properties of hooks.
Scope of the claim, stated before the numbers so it cannot be read wider afterwards. This measures one harness against ten hooks under a strict no-edit rule. It is not a survey of v4 hook quality and it is not a security review of anything. It also does not establish that other harnesses would fare the same; only one was run. It establishes a floor: on the population as it actually exists, a generic invariant harness pointed at an unchosen hook does not currently run.
The headline
2. The headline, and it is our own defect
On the first cold pass, ten of ten hooks failed at the same step, and the step was ours.
Our own one-command adopter entry point generates a Solidity test file for the hook under test. It emitted a hard-pinned pragma that our own compiler pin could not satisfy. Every run died there, before the harness ever reached the hook. Verbatim, the failure that appeared in all ten logs:
caliper: FAIL at step 'adopter-clean-leg' (rc=1).
Error: Encountered invalid solc version in test/bring-your-hook/adopters/CaliperRun.t.sol: No solc version exists that matches the version requirement: =0.8.26
clean-passes: FAIL (forge test rc=1)
Source: T-P034-A §4.1. The generator's line, verbatim:
$ grep -n "pragma solidity" caliper
334:pragma solidity 0.8.26;
against a project pinning solc_version = "0.8.33". In Solidity a bare
pragma solidity 0.8.26; means exactly =0.8.26, so the generator produced a
file the project's own compiler could not accept, for every input, including inputs that were
otherwise fine.
Why we did not know. Our continuous integration was green and had been green. It was green because no job on the branch invoked the generator. The bundled no-argument demo generates nothing. The two hand-written adopter suites generate nothing. The one code path that a stranger's hook would travel was the only path nothing exercised, so it could break and stay green.
That last sentence was subsequently proven by measurement. An independent reviewer
(T-P034-S2) ran five mutations against the repaired tree, two of the reviewer's own
design. Mutation M2 reverted one of the two repairs and then ran BOTH legs on a tree matching a fresh
checkout. Verbatim from the S2 result, §1, mutation M2:
M2_EXIT=1
434:Error (6275): Source "lib/v4-hooks-public/lib/v4-periphery/src/utils/BaseHook.sol" not found: File not found.
[the line continues with a searched-locations list; trimmed here because it carries an absolute
local path containing the operator's name. Untrimmed in the T-P034-S2 result, line 102.]
445:adopter-generator-leg: FAIL (./caliper run rc=1)
$ rm -rf src/adopters/caliper-run test/bring-your-hook/adopters
$ ./ci/clean_leg.sh
M2_CLEAN_LEG_EXIT=0
[one per-suite result line omitted here; T-P034-S2 line 116]
Ran 9 test suites in 6.93s (42.71s CPU time): 43 tests passed, 0 failed, 0 skipped (43 total tests)
clean-passes: OK
The pre-existing repo-wide clean leg stayed green, 43 tests passed, 0 failed, while the new leg that exercises the generator went red, exit 1, on the same tree. That is a direct demonstration that the existing green was structurally blind to this class of defect. It is not an inference from the defect's survival; it is the blindness reproduced on demand.
One more result belongs here, because the first objection to any CI-based claim is that a
green means nothing. Mutation M3, of the reviewer's own design, changed the generator to
emit an exact 0.8.33 pin. That still compiles under the project's pin and still
completes the full walk. The new leg went red anyway, on an artifact assertion rather than on the
exit code. Verbatim from the S2 result, §1, mutation M3:
M3_EXIT=1
442:[PASS] invariant_byoh_observables_match_ledgers() (runs: 256, calls: 12800, reverts: 0)
482:adopter-generator-leg: FAIL (generated pragma is an exact pin again; W1 regression):
483:2:pragma solidity 0.8.33;
The process exited 0, the 12,800-call walk passed, and the leg still failed. So the leg is provably not exit-code-only.
The honest scope of that CI leg, at the depth it was measured and not at the depth it was described. The reviewer's mutation M4 truncated the harness's import-graph walker to depth 1 and the leg stayed green:
M4_EXIT=0
adopter-generator-leg: OK (generator ran, generated pragma is ranged, sibling vendored, BYOH walk passed)
The fixture's own import graph is depth 1, so recursion past depth 1 is not covered by the leg, and two source comments in the repair describing the vendoring as "recursive" over-claim. That is the reviewer's finding F1, non-blocking. This census is partly a document about CI legs that look like they prove more than they prove. Separately, the reviewer's finding F2 records that the repair's prediction of WHICH assertion would catch a vendoring regression was wrong: the compile fails first. The leg still goes red; only the prose prediction was inaccurate.
Nothing in this section names a hook, and nothing in it is about a hook. The largest single wall in this census, hitting ten of ten, is our own generator's pragma.
The sampling rule
3. The sampling rule, published in place of the table
We do not print the ten hooks by name or by address. We print the rule that produces them instead, and the rule is deterministic, so a reader regenerates the exact same ten.
This is a method choice, not a withholding. The value of a named table is reproducibility. Reproducibility is fully preserved by a deterministic rule, and the rule carries something a table cannot: it is checkable that the selection was not steered. A table of ten hooks proves nothing about how those ten were picked. A rule written down before the data was fetched proves exactly that.
The rule was written and committed to its result file BEFORE any hook data was fetched or inspected. That is the fact that makes this number a measurement rather than a selection. Had the rule been written after the data was seen, no amount of transparency about the rule would recover that property.
The rule as pre-declared, with four clauses redacted in place
The four redactions below are name-removals only: two hook names, an internal invocation string with its repo path and branch, an internal environment-variable prefix, and an internal escalation target. R1 through R4, which are the clauses that determine which ten hooks the rule produces, are unmodified, so the regenerability argument above survives the redactions intact.
R1. Frame definition.
Frame = every entry inhttps://raw.githubusercontent.com/Uniswap/hooklist/main/hooklist.jsonthat satisfies BOTH:
- (a) carries at least one permission/flag whose key matches the case-insensitive regex
.*returnsdelta$with a truthy value. Concretely the four v4 delta flags:beforeSwapReturnsDelta,afterSwapReturnsDelta,afterAddLiquidityReturnsDelta,afterRemoveLiquidityReturnsDelta.- (b) whose hook address, lowercased, does NOT appear anywhere in the raw text of
https://raw.githubusercontent.com/Uniswap/routing-api/main/lib/util/hooksAddressesAllowlist.ts.R2. Dedup key. Lowercased
0x-prefixed hook address. One row per unique address. A hook deployed on several chains counts once. Frame size F = count of unique surviving addresses.R3. Ordering. Frame sorted by hook address interpreted as an unsigned integer, ascending. Deterministic, no ties possible (addresses are unique by R2).
R4. Stride selection. Target N = 10. Stride
s = floor(F / 10). Primary candidate indices (0-indexed into the sorted frame) arei_j = j * sforj = 0..9.R5. Replacement / skip rule. A candidate is SKIPPED and replaced ONLY for one of these reasons, each of which is logged:
NO_SOURCE: no discoverable public source repo AND no verified source on a block explorer. That is, no compilable file set can be assembled at all.PRIOR_HANDPICKED: the candidate is [two hook names redacted], the two hooks the org already hand-picked. Including them would inflatek. They are excluded by rule.DUPLICATE_IMPL: the candidate's source is byte-identical to an already-selected sample member (same contract redeployed at a new address). Counting it twice would double-count one result.Replacement walks upward from the skipped index (
i+1,i+2, ...) to the next address not already used or skipped.R6. No judgement skips. A hook with assemblable source ENTERS THE RUN regardless of how unpromising it looks: unusual shape, exotic dependencies, unfamiliar solc, huge file count, no tests, no docs. Its outcome is whatever bucket it lands in. "This one will obviously fail" is not a skip reason. This is the whole point of the ticket.
R7. Cold run. Fresh clone or fresh source assembly of the target. Zero edits to target source. Invocation is exactly [internal invocation string, repo path and branch redacted; it is the harness's documented one-command adopter entry point against the hook's entry file]. Environment overrides ([internal environment-variable prefix redacted]) are permitted ONLY where the wrapper's own documented adopter path requires them, and every override used is recorded verbatim per hook; a hook needing overrides beyond the documented set is
WALL_SHAPE, not a pass.R8. Buckets. Exactly one per hook:
RAN_CLEAN,RAN_FLAGGED,WALL_DEPS,WALL_SHAPE,WALL_SOURCE,OTHER.k= count ofRAN_CLEAN+RAN_FLAGGED, i.e. runs that completed end to end and produced a meaningful invariant result.R9. Disclosure rail. Any
RAN_FLAGGEDon live deployed third-party code halts investigation of that hook immediately, is NOT written up in detail here, and escalates to [internal escalation target redacted].
R5's PRIOR_HANDPICKED clause never fired: neither previously hand-picked hook landed on
a stride index. The sample contains zero hooks this project had previously touched.
The frame, recomputed
Both source files were fetched 2026-08-01 by the measuring ticket and not inherited from any prior count:
HTTP:200 BYTES:597390 (hooklist.json)
HTTP:200 BYTES:24787 (hooksAddressesAllowlist.ts)
Applying R1 through R3, verbatim script output:
TOTAL ENTRIES: 476
UNIQUE ADDRESSES ALL: 415
ENTRIES WITH ANY DELTA FLAG: 331
UNIQUE DELTA ADDRESSES: 280
DELTA ADDRESSES PRESENT IN ROUTING ALLOWLIST: 67
FRAME SIZE F: 213
F = 213. The subtraction producing it is 280 - 67 = 213, both operands
from the lines above. Stride, verbatim:
F= 213 STRIDE s=floor(F/10)= 21
So: stride 21, indices 0, 21, 42, 63, 84, 105, 126, 147, 168 and 189 into the address-ascending frame. Those ten indices, applied to the frame the rule defines, are the ten hooks in this census. Anyone can regenerate them.
Skips required: zero
Skips required to reach N = 10: ZERO.
All ten primary stride candidates had verified source retrievable from a public Blockscout instance
(eth.blockscout.com) with no API key, on the first try. Source availability was NOT a
binding constraint on this sample. Whether the frame members this census did not sample are equally
reachable is UNPROBEABLE from n=10; they were not fetched.
One consequence of withholding the table
Because the rule is deterministic and published, publishing per-index outcomes ("the hook at index 63 failed at X") would re-identify each hook exactly as effectively as printing the table. So this page carries aggregate counts only, and no per-index breakdown. The per-index detail exists internally and is not published. That is the same decision as the table decision, applied consistently.
The two numbers
4. The two numbers: cold before repair, cold after repair
Both numbers are the same ten hooks, the same invocation, the same branch, the same strict cold rails (zero edits to target source, no per-hook remapping, no per-hook compiler pin, no hand-supplied constructor arguments).
| Cold BEFORE repair | Cold AFTER repair | |
|---|---|---|
| k / N | 0 / 10 | 0 / 10 |
| Source | T-P034-A §4.1 | T-P034-S1 §4 |
RAN_CLEAN | 0 | 0 |
RAN_FLAGGED | 0 | 0 |
WALL_DEPS | 0 | 7 |
WALL_SHAPE | 0 | 3 |
WALL_SOURCE | 0 | 0 |
OTHER (our own defect) | 10 | 0 |
Cold before repair: 0 / 10
Every one of the ten exited 1 at the same step, on our generator's pragma, quoted in §2 above.
Verbatim exit codes, all ten, from T-P034-A §4.1 (hook labels replaced by their stride
index j, per §3's consistency rule; the labels are in the internal appendix):
EXIT=1 j=0
EXIT=1 j=1
EXIT=1 j=2
EXIT=1 j=3
EXIT=1 j=4
EXIT=1 j=5
EXIT=1 j=6
EXIT=1 j=7
EXIT=1 j=8
EXIT=1 j=9
Bucket tally at this pass, verbatim from T-P034-A §5: OTHER 10, all the
same harness-side pragma defect.
Cold after repair: 0 / 10
This number was produced by the S1 re-run, not by me. It is pointed at rather than
restated as my own work: T-P034-S1 §4, headline line k / N = 0 / 10, with
the per-hook exit codes at the same section printing EXIT=1 for all ten indices. The
bucket tally line, quoted from T-P034-S1 §4:
Tally: WALL_DEPS 7, WALL_SHAPE 3, WALL_SOURCE 0, RAN_CLEAN 0, RAN_FLAGGED 0, OTHER 0.
The number did not move. What moved is where the ten runs die. Before the repair,
all ten died on our code before reaching the hook. After the repair, OTHER is empty and
all ten failures are measured properties of the live hook population: its exact compiler pragmas, its
dependency tails, its import-prefix conventions, its constructor shapes.
Two proofs that the repairs actually took, both quoted from T-P034-S1 §4:
$ grep -l 'No solc version exists that matches the version requirement: =0.8.26' /tmp/p034s1/logs/*.log
W1_STRING_FILES=[two log filenames redacted; they name their hooks. Unredacted in the internal appendix
per D-P034-census-publish-2026-08-01. The count is two.]
Zero of ten now die on the generated test file. The two logs still carrying that error string carry it against the TARGET's own file, because those two hooks pin that exact compiler version themselves. And:
$ grep -c 'BaseHook.sol" not found' /tmp/p034s1/logs/*.log
returns 0 for all ten logs. The second repaired wall is gone from the entire sample.
The 2 / 10 that is available and why it is not reported
0 / 10 after repair is the finding. It would be easy to lead with "2 / 10 after
cumulative hand repair", which is a real number from a diagnostic pass in T-P034-A §4.3,
and it would be misleading, for two reasons. First, that 2 / 10 required a wrapper change, a
remapping redirect, and hand-supplied constructor arguments per hook, which is not a cold run and is
not what an adopter would experience. Second, and worse, both of those greens checked no
hook-specific property at all, for the reason §7 of this page sets out.
The census is valuable precisely because it is the only dataset anyone appears to hold on whether Uniswap's own guidance is achievable in practice on the live hook population. A flattering version of it would be worth nothing.
What the number does and does not support
- Supported: on a systematic, pre-registered sample of a 213-member frame, a generic invariant harness pointed at an unchosen hook under no-edit rails completed zero runs, both before and after two repairs to the harness.
- Not supported, marked
UNPROBEABLE: the exact rate over all 213 frame members. n = 10 gives a point estimate and this page computes no confidence interval. The frame members outside the sample were not run. - Not supported: any statement about other harnesses, other tools, or other approaches. This measures one harness.
The wall taxonomy
5. The wall taxonomy W1 through W7, before and after
Seven distinct walls stop a generic harness from reaching an unchosen hook. They were named in
T-P034-A §5 and §6 and re-counted on the post-repair cold run in T-P034-S1
§4 and §5.
| # | Wall | Whose problem it is |
|---|---|---|
| W1 | The harness's generator emits a hard-pinned compiler pragma the project's own pin cannot satisfy | Ours |
| W2 | A dependency prefix in the harness points at a checkout that does not contain the file the population imports | Ours |
| W3 | A library the hook imports is absent from the harness's tree entirely | Environmental, unbounded |
| W4 | Import-prefix collision: the hook and the harness use the same prefix rooted differently | Architectural |
| W5 | The hook's constructor takes arguments the scaffold does not supply | Population shape |
| W6 | The hook reverts because the default pool does not satisfy an access gate it enforces | Population shape |
| W7 | The hook's own source pins an exact compiler version older than the harness's substrate | Architectural |
Counting method, stated before the counts
Two different countings are reported and they are not interchangeable:
- Binding count. The one wall that actually stopped that run. Exactly one per hook, so the column sums to 10. This is what "how many hooks did this wall stop" means.
- Affected count. Hooks where the wall is present at all, including where it sits queued behind an earlier wall and would have fired next. This column does not sum to 10 and is not meant to.
Per-index breakdowns are withheld per §3. Only counts appear here.
The table
| # | Binding, cold BEFORE repair | Binding, mask-removed diagnostic | Binding, cold AFTER repair | Affected, cold AFTER repair |
|---|---|---|---|---|
| W1 | 10 / 10 | 0 (removed by the diagnostic probe) | 0 | 0 |
| W2 | masked by W1 | 2 | 0 | 0 |
| W3 | masked by W1 | 4 | 4 | 4 binding, plus one queued behind W7 |
| W4 | masked by W1 | 1 | 1 | 1 |
| W5 | masked by W1 | 1 | 3 | 3 |
| W6 | masked by W1 | 0 | 0 | queued behind W5 on one hook |
| W7 | masked by W1 | 2 | 2 | 2 |
| 10 | 10 | 10 |
Provenance for each column.
- Cold before repair, binding. All ten stopped at W1. Source:
T-P034-A§4.1, tally line "Pass 1 tally ...OTHER10, all the same harness-side pragma defect", plus the tenEXIT=1lines quoted in §4 above. - Mask-removed diagnostic, binding. This column is a CLASSIFICATION I made, one
row at a time, from the verbatim first-fatal error line recorded for each hook in
T-P034-A§5. The error lines are verbatim tool output; the assignment of each line to a wall class is mine and is marked as such rather than presented as a tool result. The bucket tally underneath it is verbatim: "Pass 2 tally:WALL_DEPS9,WALL_SHAPE1,WALL_SOURCE0,RAN_CLEAN0,RAN_FLAGGED0,OTHER0." - Cold after repair, binding and affected. Lifted, not re-derived, from
T-P034-S1§5's own summary sentence, which states the counts directly: W3 4 hooks with 1 more queued behind W7, W4 1 hook, W5 3 hooks, W6 queued behind W5 on 1 hook, W7 2 hooks, and W1 and W2 zero hooks because they are repaired and CI-pinned. The source sentence is index-keyed and is therefore quoted verbatim in the internal appendix rather than here, per §3: an index-keyed result is a named result once the sampling rule is published. The counts above are that sentence's own counts, not a recount. The affected column restates that sentence's enumeration and adds no total.
What the shape of the table says
Before the repair, the census could not see the population at all. One defect of ours sat in front of everything and returned the same answer ten times. A number produced in that state carries no information about hooks; it carries information about us.
After the repair, the census sees the population, and the population is what stops it. Ten failures, four distinct wall classes, and not one of them is the defect that produced the first number. Two of the four are our architecture. That is the dataset. The number is identical and its meaning is completely different.
Two of the four remaining wall classes cannot be retired by adding more configuration. W4 was proven mutually exclusive by direct probe rather than argued. Setting the harness's import prefix to a hook's convention breaks the harness's own files:
Error (6275): Source "lib/v4-core/src/src/libraries/FullMath.sol" not found: File not found.
Error (6275): Source "lib/v4-core/src/src/interfaces/IHooks.sol" not found: File not found.
The doubled src/src is the collision. These two configurations cannot coexist in one
Foundry project. W7 is the same shape in the compiler dimension: a single global compiler pin
excludes every hook whose own source pins a different exact version, and any single global pin
excludes somebody.
After repair W4 binds 1 hook and W7 binds 2. The conclusion that follows is an architecture statement, not a bug list: one fixed project with one import table and one compiler pin is the wrong container for an arbitrary hook population. Serving the population needs a generated per-hook project with its own import table and its own compiler, taken from the hook's own source. That is a rebuild, not a patch.
Cost to retire
6. What each wall costs to retire
Costs are in agent wall-clock, taken from T-P034-A §6. The calibration base stated
there: building the frame, fetching ten hooks, running three passes and writing the result up was
work of the order of one agent session, so "one session" below means work of that order. These are
estimates, not measurements, and are labelled as such. Two of the seven have since been executed and
their real cost is noted.
| # | Fixable? | Estimated cost | Status |
|---|---|---|---|
| W1 | Yes, trivially | Under 15 agent-minutes for the edit; about half an agent session including the CI leg that stops the class recurring | DONE. Executed in T-P034-S1. One character class in the generator. Reviewed and passed in T-P034-S2. |
| W2 | Yes | Under 30 agent-minutes | DONE. Executed in T-P034-S1. One import-prefix line redirected to a checkout already present in the tree. Full clean re-run showed no regression: Ran 9 test suites ... 43 tests passed, 0 failed, 0 skipped (43 total tests). |
| W3 | Yes for any one library, never as a class | One agent session for the specific library; unbounded as a class | Open. Every new hook can introduce a new dependency. This tail does not converge. |
| W4 | No, not by pinning | Not fixable in the current architecture. A real fix is 3 to 5 agent sessions | Open, structural. Requires either a generated per-hook project with its own import table, or rewriting the hook's import lines, which means editing the target and changes what the tool is. |
| W5 | Partly | 10 to 30 agent-minutes per hook, forever; 1 to 2 agent sessions to partly automate | Open. The documented override exists and works. Knowing what to put in it is per-hook judgement, and it cannot cover arguments that must be live contracts. |
| W6 | Only per hook | Half an agent session per hook, no general fix | Open. Any access-gated hook does this. The documented overrides exist; knowing what to put in them means reading the hook. |
| W7 | No, not without editing the target | Not fixable under the no-edit rule. A multi-compiler build is roughly 1 to 2 agent sessions | Open, structural. Foundry supports per-file compiler selection, so this is tractable, but it means dropping the single global pin. |
The shape of the cost. W3, W5 and W6 are per-hook taxes that never stop being paid. W4 and W7 are the structural pair: they cannot be retired by adding more pins, and they are the reason the honest recommendation out of this census is a different container rather than a longer configuration file.
Actual coverage
7. What the harness actually covers
This is the section a reader should check first, because it is the one where a tool in this category is most likely to overstate itself.
The only coverage language this project uses, and it is used here verbatim:
one generic property (settlement liveness / the delta screen, 256 runs at depth 50, 12,800 calls
through the real PoolManager) plus a declared-property harness with seven worked cases.
Out of the box, with no adopter declaration, the declared-property harness asserts
nothing. That is not a figure of speech. The default observables list is empty. Verbatim,
from test/bring-your-hook/BYOHInvariantBase.sol:132:
function _observables() internal view virtual returns (Observable[] memory) {
return new Observable[](0);
}
The one generic invariant function iterates that array. With zero declared observables it compares zero pairs. It is not weak; it is vacuous until an adopter declares something.
So what actually runs for free is the settlement screen, and it is real: the
invariant configuration sets runs = 256, depth = 50,
fail_on_revert = true, which is 12,800 fuzzed calls through the real
PoolManager with no mock and no fork. That is coverage-guided search, not exhaustive
proof. Any call the hook reverts, leaves unsettled (CurrencyNotSettled), or answers with
a wrong selector fails the run. The harness's own docstring has always said this, verbatim:
/// settlement liveness. `fail_on_revert = true` means any fuzzed
/// swap or liquidity modification your hook reverts, leaves
/// unsettled (`CurrencyNotSettled`), or answers with a wrong
/// selector fails the run. That is the flash-accounting protocol
/// check (the H3 class) for free.
The practical consequence. A hook author who runs this tool and sees green has been
told that their hook does not break PoolManager settlement under a two-token pool with
no hook data. They have not been told that their hook's accounting is correct. Declaring the
properties that would tell them that is the bespoke work, and it is theirs to write. The seven worked
cases exist to be copied from, not inherited.
On Uniswap's section 7, and what it is not
Uniswap's section 7 says "properties such as" and its section 3 says "e.g.". It is an illustrative list, not a closed conformance specification, and this page does not render it as one. Framing it as a three-item spec that a tool can tick would misrepresent Uniswap as well as the tool. The wording invites hook authors to fuzz for the properties that matter to their hook; it does not enumerate a fixed set.
That distinction cuts against us as much as for us. It removes any basis for claiming conformance to a spec, because there is no spec to conform to.
What the repairs did
8. What the repairs did, and what they did not do
Two walls were repaired between the two cold runs on this page. Being precise about what that bought matters, because there is a wrong framing available in each direction.
The wrong framing in one direction: that the repairs would raise the number. They did not, and an earlier internal projection that they would was falsified by measurement. The projection had silently assumed the hand-repair override set from a diagnostic pass. Under strict cold rails, the hooks the second repair unblocks are stopped immediately afterwards by constructor arguments, which is W5. The correction is on the record.
The wrong framing in the other direction: that the repairs were therefore a null.
They were not. Before them, the census had no view of the population; every run died on our code.
After them, OTHER is empty, every one of the ten failures is a measured property of the
live population, and the defect class is pinned by a CI leg proven to go red on demand (§2, mutations
M2 and M3).
The true statement, and it is the one this page makes: the repairs landed, both repaired walls left the sample entirely, and the number held at its floor because what remains is the population.
What is not asserted
9. What this census does not assert
No security finding is made against any protocol, project, contract or team, named or unnamed. It is a description of what was measured.
- Zero
RAN_FLAGGEDoccurred at any pass, in either cold run or in any diagnostic pass. The disclosure rail R9 never fired at any point. - Seven of the ten failures are compile-time or configuration-time
(
WALL_DEPS7, from the after-run tally in §4). They are our harness failing to assemble a buildable project around someone else's code. A file that our tree does not contain is a fact about our tree. - The other three are
setUp()failures (WALL_SHAPE3, same tally), where a constructor takes arguments our cold invocation is not permitted to supply. All three compiled clean first. A constructor taking arguments is normal engineering, not a defect. - No hook's logic was reviewed, reasoned about, or reached. In the after run, no hook's invariant walk executed at all. There is nothing to have found.
The direction of every finding on this page points at the tool, not at the population. Where a wall is environmental (a library our tree lacks, a prefix convention that differs from ours), that is stated as a property of the interface between two projects, and the interface is ours to build.
A reader who takes any sentence on this page as evidence about a specific protocol's security has misread it, and we would rather say so here than be quoted that way later.
Limits and provenance
10. Limits, UNPROBEABLE items, and the provenance ledger
Marked UNPROBEABLE
| Claim | Why it is not probeable here |
|---|---|
| The completion rate over all 213 frame members | n = 10. The frame members outside the sample were not fetched or run. This page computes no confidence interval. |
| Whether the frame members outside the sample have retrievable source | Ten of ten did, on the first try. That is a bound on the sample, not on the frame. |
The CI wall-clock of a full 12,800-call adopter walk on a hosted ubuntu-latest runner | Measuring it requires pushing the branch and letting a hosted runner execute. No push was permitted by any ticket in this chain. The only measured runtimes in this project are local, on Apple Silicon, with a warm compiler cache. Named here so that silence is not read as clearance. |
| How this harness compares to any other invariant harness | No other harness was run. |
Limits a reader should hold against the number
- n = 10. Systematic, pre-registered, unsteered, but ten.
- One frame. The frame is delta-flag hooks not in the routing allowlist. Hooks without delta flags were out of frame by rule and are not represented.
- One harness, one branch, one host. A different toolchain pin would move W7's count directly.
- The no-edit rule is strict by design. Permitting edits to target source would raise the number and would measure something else, namely how much bespoke work a motivated engineer can do per hook. The diagnostic pass that permitted hand repair reached 2 of 10, and what those two greens actually screened is §7's subject. That is why the strict number is the one reported.
- The CI leg proves its class at the depth it was mutated, not deeper. Import-graph recursion past depth 1 is not covered (§2, mutation M4).
Provenance ledger
Every number on this page and where it came from.
| Number | Source |
|---|---|
HTTP:200 BYTES:597390, HTTP:200 BYTES:24787 | T-P034-A §2, fetch output |
TOTAL ENTRIES: 476 through FRAME SIZE F: 213 | T-P034-A §2, script output |
F= 213 STRIDE s=floor(F/10)= 21 | T-P034-A §2, script output |
| Skips required: zero | T-P034-A §3 |
Cold-before k / N = 0 / 10; OTHER 10 | T-P034-A §4.1 and §5 |
The generator pragma line 334:pragma solidity 0.8.26; | T-P034-A §4.1, grep -n output |
| The pass-1 failure string | T-P034-A §4.1, run log |
Mask-removed diagnostic bucket tally, WALL_DEPS 9 / WALL_SHAPE 1 | T-P034-A §5 |
| W1..W7 cost column | T-P034-A §6. Estimates, labelled as such. |
The src/src collision probe output | T-P034-A §5.1 |
_observables() default body | T-P034-B §B4.2 and T-P034-A §4.4, both quoting BYOHInvariantBase.sol:132 |
runs = 256, depth = 50, fail_on_revert = true | T-P034-B §B4.2, foundry.toml lines 99-102 |
| Uniswap §7 and §3 quotations | T-P034-B §B4.1, read live 2026-08-01 from developers.uniswap.org/docs/protocols/v4/security |
Cold-after k / N = 0 / 10; tally WALL_DEPS 7, WALL_SHAPE 3 ... | T-P034-S1 §4. Not my run; pointed at, not restated. |
| Post-repair grep results for the two repaired error strings | T-P034-S1 §4 |
43 tests passed, 0 failed clean-leg re-run | T-P034-S1 §2 and independently T-P034-S2 §1 and §6 |
| After-repair wall counts W3 4 / W4 1 / W5 3 / W7 2 | T-P034-S1 §5, summary sentence, lifted verbatim |
| The §5 "Affected, cold AFTER repair" column | T-P034-S1 §5, same summary sentence. Its enumeration restated, no total added. |
M2_EXIT=1, M2_CLEAN_LEG_EXIT=0 | T-P034-S2 §1, mutation M2 |
M3_EXIT=1 with the passing 12,800-call line | T-P034-S2 §1, mutation M3 |
M4_EXIT=0 | T-P034-S2 §1, mutation M4 |
The one derived column on this page is the mask-removed diagnostic binding-wall column in §5. It is a classification of verbatim error lines, made by me, and marked as mine at the point of use. Everything else is lifted.