Component-based vault tokenization with credit/debt tracking. 12 in-scope files, 1,926 lines, read in full. Six leads opened, six closed, zero submissions.
init()Hypothesis: AccountERC20Tracker.init() and Global.init() have
no access control. That's the textbook proxy-initialization front-run: deploy the proxy, and
whoever calls init first owns it.
Closed: there is no window. Deployment always wraps init inside the same
atomic construction delegatecall, so the proxy is never observable in an
uninitialized state.
ComponentBeaconFactory.sol:59-63
Deploy.s.sol:129-132
Also already covered by an external review (ChainSecurity, September 2025) — worth checking before spending hours on a path that a paid audit has walked.
getSharePrice() returns 1e18 by defaultHypothesis: when lastShareValue == 0 the function falls back to
1e18. That conflates two very different states — "no shares issued yet" and "real
value has rounded to zero" — and the second one, priced at par, is a mispricing.
Closed: documented and explicitly asserted behaviour
(ValuationHandler.t.sol:216-218), and the trigger isn't attacker-reachable in the
first place: the path is onlyAdminOrOwner. A deliberate, tested default guarded by
privileged access is a design decision, not a bug.
OpenAccessLimitedCallForwarder.executeCalls()Hypothesis: an unauthenticated entry point that executes calls to whitelisted targets reads like broken access control.
Closed: intentional, and the code says so in its own header (lines 20-21). The name is accurate — access is open by design, the limit is the target whitelist, and the documented operator responsibility is not to let ETH accumulate at that address. The residual risk is operational, and the team has stated it.
Access control, checks-effects-interactions ordering, and numeric bounds
(int128/uint32) across FeeHandler, both fee trackers, and
LinearCreditDebtTracker. Read in full; no gap found. Nothing more specific to report,
which is itself the report.
Zero findings. Zero submissions. All 12 in-scope files read completely, so there is no "continue next session" remainder here — this target is closed.
What changed in my method afterwards. This was the second clean linear read in a row. Two zeroes don't prove the code is clean and they don't prove the reviewer is bad; they do mean it's time to check the method rather than repeat it a third time. The change: read the protocol's own invariant suite first, write down what it asserts, then go looking for the invariant it doesn't assert. Bugs live in the gap between what a team believes and what it tests — not in paths that have already been fuzzed a thousand times.
Reviewed 2026-08-24. All work from public source. Not affiliated with Enzyme.
The clone carried a real .claude/settings.json that auto-runs a command whenever
an AI agent edits a Solidity file in it. Benign here — but it's a general hazard worth knowing
about if you point coding agents at unfamiliar repositories.
I wrote it up separately →