Ethereum contracts deployedTransactions may use real assets. Primary-market minting and instant redemption are KYC-gated; deployment does not mean every product or entry path is open. Review the legal and risk disclosures.

Full-system audit, round nine

Internal review. This is an internal engineering review of source, not an independent external audit, and it does not by itself authorize a production launch. It reviews the source at the baseline below, which is not necessarily identical to the code deployed on any network. Forest Road Vault is live on Ethereum mainnet; its Solana curator vault remains on devnet and BSC has its own deployment status. An open finding applies only to the product, version and deployment state named by this review. Each review's scope bounds what its clean result is worth. Nothing here is a securities-law representation; token characterization is a matter for counsel.

Scope
The entire codebase again, every production contract and all frontend functionality, plus the fixes from the two preceding rounds.
Method
Nine module surfaces reviewed twice by independent agents with opposing briefs, plus four new lenses that follow a single entity through its whole life rather than reviewing by module. Then two adversarial refuters per candidate and a completeness critic.
Reviewed baseline
48e1325
Internal report
audit-reports/FULL_SYSTEM_AUDIT_2026-07-28.md (round 9 addendum)

Findings and remediation history

  • C-11MediumRemediated

    Raising the redemption floor during a live settlement could permanently freeze the queue | a residual on the previous round's fix

    The previous round stopped a settlement committing while it could never reach the economic floor. It did not stop the floor moving underneath a settlement that had already legitimately committed. Because the liquidity budget is captured once and can only shrink, raising the floor mid-settlement made both guards permanently unsatisfiable, the same dead end reached through a governance setter rather than a chunk boundary. The previous round's own regression test could not catch it, because it sets the floor before settling. Fixed by capturing the floor alongside the budget when a settlement opens, so a live settlement is judged against the parameters it opened under and a change takes effect on the next one. Proven reachable against the unfixed code before the fix was written.

  • C-07MediumOpen

    A reserve-level write-down has no cascade absorber, and the backing assertion then blocks ordinary operation

    One reserve primitive lowers backing without a paired token burn and without asserting the backing invariant, the exception to the pairing rule the design documents state. Because the protocol operates at zero headroom, any resulting shortfall then blocks minting, redemption, yield recognition and servicing until governance acts, and the loss cascade has no path to absorb a loss that occurred at the reserve layer rather than in the loan book. The primitive currently has no callers. One part of the original filing was withdrawn: relaxing the backing assertion to permit delta-neutral redemption was already considered and rejected in an ADR, because it inverts the loss cascade.

  • C-19MediumOpen

    CONTESTED: the yield cap leaves a healthy vault at the exact top of the accepted skim band

    Published as contested because it turns on the precise reading of an accepted trade-off rather than on disputed code. The entry guard closes only when the withheld stream is strictly greater than a set multiple of vault assets, and the cap introduced two rounds ago sets the stream to exactly that multiple, so entry stays open at the boundary rather than inside it, and a depositor arriving at that instant shares in yield already earned by incumbents. The band itself is an accepted residual from an earlier round; what is new is that a healthy vault now sits at its very top by construction rather than transiently. This is a calibration question for economic review, not a coding error.

  • C-01LowRemediated

    The storage-layout check the upgradeability ADR asserts did not exist, and the per-slot test added last round covered one namespace of sixteen

    Found independently by five reviewers, each arriving from a different contract. The previous round's response to a storage-layout risk pinned exactly one struct, leaving fifteen namespaces in the prior posture, and the verification command the contracts' own comments prescribe reports nothing for this storage pattern, because these contracts declare no ordinary state. Fixed by a check that compares the field order and types of every namespaced and array-element struct against a committed baseline, wired into the pipeline: inserts, reorders, retypes and deletions fail; extending a namespaced root at its tail passes; any growth of an array-element struct fails, because those are laid out contiguously. The ADR now describes the check that exists.

  • C-02LowOpen

    Pausing the attestation oracle transitively halts the loss cascade while leaving both exits open at an unimpaired price

    Loss realization, payment recording and past-due curing each require a freshly minted attestation, and the submission path is pausable. A guardian pause therefore suspends loss recognition, which the design documents describe as never pausable, while redemption and the queue continue pricing against a book that cannot be marked down. The asymmetry is the finding: not that the pause exists, but that it stops the marking without stopping the exiting.

  • C-03LowOpen

    The token pause halts every inflow but leaves the redemption path draining the reserve

    Burns deliberately bypass the pause so the loss cascade cannot be frozen, a fix from an earlier round. The consequence is that a token pause stops minting, staking and yield routing while the direct redemption path, which is a burn plus a transfer out of reserves, stays fully open. The compensating symmetry the governing documents rely on no longer holds, and neither contract consults the other's pause state.

  • C-05LowOpen

    The outflow cap restarts the vesting clock on every qualifying settlement, deferring yield already earned

    A residual on the previous round's fix. The cap does not merely clamp the retained amount, it re-stretches it over a fresh full vesting window; and because the inflow cap leaves no slack at the boundary, an ordinary settlement re-triggers it. Permissionless settlement can therefore defer recognition of yield that has already been realized. The deferral is conservative in direction and the retained amount is non-increasing, but it does not converge as quickly as assumed when the fix was written.

  • C-06LowOpen

    The outflow cap runs after assets leave, so equal-ranking exits in one settlement can be priced differently

    The same fix places its re-cap after the withdrawal completes, and the settlement loop re-reads the vault price on every fill. A later fill in the same settlement is therefore priced against a value the previous fill's own re-cap stepped up. The strict-FIFO head, the position that waited longest, is priced worst. Display-neutral and small in ordinary conditions, but it breaks the pari-passu expectation within a single settlement.

  • C-09LowOpen

    Documentary mint-gate attestations are existence-only and can be satisfied in advance for facilities that do not exist yet

    The submission path accepts any facility identifier, never checks that the facility exists, and imposes no payload requirement on the documentary kinds. The mint gate then asks only whether an attestation of that kind exists at the identifier about to be minted. The binding that does hold is the separate terms attestation, which is unconditional, so the residual is that the documentary facts are proved to exist rather than proved to be about the facility in question.

  • C-10LowOpen

    A non-renewable facility that outlives its maturity can never be amended again, and its full balance stays permanently markable

    Found by the facility-lifetime lens, and visible only if a facility is kept alive past its maturity. Only two functions can move a schedule forward: one cannot push past maturity, and the other rejects any amendment that would extend a non-renewable facility. Once maturity has passed, every admissible amendment necessarily exceeds it, so the workout has no on-chain path to reschedule, while the outstanding balance remains permissionlessly assertable into the senior impairment mark.

  • C-13LowRemediated

    The impairment-source setter accepts any address with no interface probe, on a read path that deliberately does not swallow failure

    The setter performs no code or interface check, and the read path deliberately refuses to catch failure, individually correct, since silently substituting zero impairment would be worse. Together they mean one governance mis-wire freezes every senior exit price until a second timelocked proposal lands. Same shape as an already-published finding about a sibling setter. CLOSED by the ADR-0031 work: `sUSDfr.setImpairmentSource` now runs `_validateImpairmentSource`, a bounded two-selector ABI-shape probe that also enforces the ordering invariant between the two impairment views, and `clearUnreadableImpairmentSource` provides the paired recovery. Verified in Round 14.

  • C-14LowOpen

    Latent: re-pointing the vault at a new redemption queue would strand every position held by the old one

    Queued shares are held in the queue's own custody, and the vault admits only the currently configured queue as a caller. Pointing the vault at a replacement therefore leaves the old queue holding shares it can no longer redeem, with no cancel and no rescue path for the positions inside it. Unreachable today; reachable on an ordinary queue migration.

  • C-16LowOpen

    Latent: a class activated without its attestation mask would originate and fund with the documentary checks skipped entirely

    The mint gate reads the required-attestation mask and iterates it without requiring it to be non-empty, so a class whose mask was never set reads zero and the loop body never runs, on both the origination and the funding gate. The unconditional terms attestation still applies, so the economic binding survives; the documentary requirements would not. Reachable through a routine governance sequencing mistake rather than an attack.

  • C-17LowOpen

    The headline collateral ratio divides a numerator containing idle reserves by loan principal alone

    A frontend-to-contract reconciliation defect. The numerator mixes loan collateral with idle reserves, which back the portion of supply that is not on loan, while the denominator is deployed principal only, so the displayed coverage of the loan book rises without bound as the book shrinks. A correctly-based coverage figure is already computed nearby and discarded.

  • C-18LowOpen

    A receipt-wait timeout discards the transaction hash and re-arms a one-click duplicate of a non-cancellable action

    The error state carries no transaction hash, unlike the pending and success states. On a receipt timeout, set well below the underlying library's own default, the transition destroys the only reference the user had to a transaction that may still land, and re-enables the button. For a queue request, which cannot be cancelled or withdrawn, that invites a duplicate irreversible entry.

  • C-20LowOpen

    Origination events emit only a commitment hash, so maturity and the off-chain reference appear in no event

    The engineering rules require the on-chain register to be reconstructable purely from events. The origination and amendment events carry a 32-byte terms commitment rather than the terms themselves, so the maturity date and the off-chain document reference are not recoverable from the log, while the comment directly above the event, and an earlier published review, both state that the full terms are emitted.

  • C-21LowOpen

    The origination fee is read from mutable governance storage at funding time and is not part of the attested terms

    The fee is applied from live governance storage when funds are disbursed, and the borrower receives principal less that fee while the facility is booked at full principal. The signed terms bundle, which an earlier round extended specifically so the economic terms are quorum-bound, does not include it. So the cash actually disbursed is the one parameter the binding does not cover.

  • C-22LowOpen

    The funding gate re-checks only the time-sensitive class parameters, so tightening the draw ceiling or tenor cap does not bind an already-originated facility

    Origination enforces the class loan-to-value ceiling and maximum tenor; the funding gate reloads the same class parameters and re-checks activity, maturity, the payment date, attestations, the terms binding and mark freshness, but never re-reads those two. A facility originated under looser limits can therefore still be funded after governance tightens them.

  • C-23LowOpen

    The directional assessment binding was applied to the backstop only, so a trivial curator top-up still voids a live recovery assessment

    An earlier remediation made global backstop capacity a directional comparison, an increase tolerated, a decrease failing closed, precisely so that a permissionless contribution could not void a depositor-favourable assessment. Per-class curator capacity was deliberately left on the exact-match side, so the same class of nuisance survives through a different pool.

  • C-24LowOpen

    Curator first-loss is shown as one blended percentage although absorption is strictly per-class and non-fungible

    The dashboard reads each class pool individually, sums them, and divides by the whole book. On chain a loss draws only against its own class pool and cannot reach another. The blended figure therefore overstates subordination for any class whose pool is thin relative to its exposure.

  • C-25LowOpen

    The transparency dashboard re-reads the entire event history on every poll, and blanks its panels while it does

    The same class of defect fixed on a sibling panel two rounds ago, still present here across five event streams: no cursor, a full re-read from the deployment block every minute, both panels reset to a loading state first, and no cancellation of a superseded sweep. Request volume grows linearly with chain age.

  • C-26LowOpen

    The error-decoding drift guard never checks contracts against the interface, so a newly added custom error renders as raw hexadecimal

    The guard verifies that published copy matches the interface and that the interface matches the contracts, but not that the contracts are fully represented in the interface. One error added to the vault is already missing, and it is raised on the application's own staking path, where its own documentation says it exists precisely so the revert carries a specific, readable reason.

  • C-27LowOpen

    Latent: the testnet address table is hand-maintained with no binding to the deployment manifest

    For production every address is bound to the manifest by several independent checks. For the testnet and local profiles the verification returns before the manifest is opened, and the address table is a hand-maintained duplicate that partial environment overrides can silently mix across two deployments. Reachable on the next redeploy rather than today.

  • C-04InformationalOpen

    A workout recovery on a defaulted facility can be booked entirely as interest, leaving the impaired principal fully marked

    Distribution admits defaulted facilities and routes the interest leg through the identical path a performing loan uses, while every piece of recovery bookkeeping sits behind a non-zero principal condition. Nothing requires a post-default receipt to be booked as principal. The discretion belongs to a trusted role and overlaps an already-accepted finding about servicer discretion; recorded because the direction of the error disadvantages the junior layers.

  • C-12InformationalOpen

    The timelock delay is shorter than the only available exit, so the promised exit window does not exist in practice

    Two ADRs and the access-control matrix state that the timelock gives holders time to exit before a change takes effect. The only senior exit is queue-gated at three weeks and the staked-token exit at three weeks, against a two-day minimum delay. The protection is real as visibility and unreal as an exit window; the documents should say which one they mean.

  • C-28InformationalOpen

    The compliance check exempts every burn before consulting the sender's sanctions status

    Burns return permitted without checking the sender, so that the loss cascade and settlement can never be blocked, a sound reason. The exemption is nonetheless unconditional on the sender rather than scoped to the cascade, so any future user-callable burn path would inherit it. Latent, and a property of the token rather than of the cascade.

  • C-30InformationalOpen

    The per-event backstop coverage cap is never exercised at a fractional value by any test

    The mock backstop's cap setter has no callers anywhere, so every campaign built on that mock runs with the cap effectively unlimited and the mock's capacity degenerates to its raw balance. The cap is a real production control; the suite has never exercised it at a binding value. A test-fidelity gap rather than a contract defect, and adjacent to a test-strength item already open from an earlier round.

Historical snapshot, superseded. This narrative predates ADR-0031 and the later impairment-source liveness remediation. Statements below about missing CI, coverage, formal, fork, or render evidence describe that earlier point in time and are not the current assurance status.

What changed about how this round was run

The previous full audit ended with a specific criticism of itself: its weakness was not effort but shape. The defects it missed were the ones that require a single entity to be walked through many sequential operations across a module boundary, each function correct in isolation, the fault living only in their composition. Seventeen of eighteen reviewers missed one such defect entirely.

So this round kept the nine module surfaces, each still reviewed twice by independent agents with opposing briefs, and added four lenses that follow one entity through its whole life rather than reviewing by module: a single facility from origination through a dozen sequential repayments to maturity and through the parallel default path; a single senior position from deposit through several rate epochs and a loss to settlement and claim; junior capital across repeated losses and recoveries; and a lens for scale and unbounded growth.

Every candidate then faced two adversarial refuters, and a critic was asked, among other things, to judge whether those new lenses had actually done what they were added for.

Whether that worked: two of four

Worth reporting honestly, because it decides how the next round should be run.

The facility and senior-position lenses worked. Each traced a real sequence, and each produced findings no module review did: a post-maturity amendment freeze visible only if you keep a facility alive past its maturity, and two defects in a recent fix that exist only across successive operations. The facility lens also named its own limit, that no test in the tree warps past maturity on a live facility, which is what a genuine lifetime review looks like.

The junior-capital and scale lenses collapsed back into module review. Their coverage statements read as file lists and static surveys. The scale lens conceded the point directly: it ran nothing, so every growth figure it gave was an arithmetic estimate rather than a measurement. Both were competent second passes over ground already covered, wearing a label they did not earn.

The result

No Critical and no High finding. Nothing found here reaches a loss of funds, an unauthorized mint, a backing break, a cascade inversion or a compliance bypass.

Eighty-four raw candidates consolidated to thirty. Two were then dropped outright by the critic, and the reason matters more than the count:

  • one would have recommended undoing an earlier fix. It described as an exploit the exact behaviour a named regression test asserts as that fix working, and its load-bearing claim, that no test composes those two calls adversarially, was simply false;
  • the other named a root cause that is causally inert, so its proposed remedy would have changed nothing.

A third had its headline struck: relaxing the backing assertion, which it proposed, was already considered and rejected in an ADR on the grounds that it inverts the loss cascade.

Twenty-seven findings remain: three Medium, twenty Low, four Informational. About half are documentation drift, an ADR asserting a check that did not exist, an invariants document describing a pause envelope the code changed, a matrix listing functions that are gone.

The clearest new defect was in the previous round's own fix

Two rounds ago a guard was added to stop a settlement committing while it could never reach the redemption floor. It did not stop the floor moving underneath a settlement that had already legitimately committed. Because the liquidity budget is captured once and can only shrink, raising the floor mid-settlement made the guard permanently unsatisfiable, the same dead end, reached through a governance setter instead of a chunk boundary.

The reason it survived is the instructive part: the regression test written for the original fix sets the floor before settling, and no stateful-fuzzing handler carries a governance selector. The test bound the path its author was thinking about and missed the one beside it.

It is fixed: the floor is now captured alongside the budget, so a live settlement is judged against the parameters it opened under, and it was reproduced against the unfixed code before the fix was written.

The finding five reviewers found independently

The previous round responded to a storage-layout risk by pinning one contract's fields to their exact slots. Five reviewers in this round, each arriving from a different contract, pointed out the same thing: that covered one namespace of sixteen, the upgradeability ADR asserted a pipeline check that did not exist, and the verification command the contracts' own comments prescribe reports nothing at all for this storage pattern, because these contracts declare no ordinary state.

That is a fair criticism of closing an instance and calling it the class. It is now closed properly: a check compares the field order and types of every namespaced and array-element struct against a committed baseline and runs in the pipeline. Inserts, reorders, retypes and deletions fail. Extending a namespaced root at its tail passes, because that is safe. Any growth of an array-element struct fails, because those are laid out contiguously and growing one shifts every later element on a live proxy. The ADR now describes the check that exists.

One finding published as contested

The vault's entry guard closes only when the withheld yield stream is strictly greater than a set multiple of vault assets, and a cap introduced two rounds ago sets the stream to exactly that multiple. So a healthy vault now sits at the very top of an accepted trade-off band by construction, rather than passing through it transiently.

Whether that matters is a calibration question for economic review, not a coding error, and the reviewers split on it. It is published as contested rather than resolved, because presenting a genuine disagreement as a settled finding would be the more misleading choice.

What this round establishes, and what it does not

It establishes that nine rounds of increasingly specialised reading now return documentation drift and remediation-completeness gaps rather than defects. That is a meaningful result about the contracts, but it is also the signal that source review has converged.

It does not establish that the contracts behave correctly, because nothing was executed during the review: every figure in every finding is hand-derived. The only measurements are the suites run alongside it, which themselves run one stateful campaign against an unbounded mock and skip the environment-gated fork tests in the pipeline. It does not establish frontend behaviour, because there is still no render harness. And it does not touch the economic calibration, the coverage cap level, the first-loss target, the vesting ratio whose acceptance rationale one finding here shows a later change falsified, which remains a human decision.

The honest read is that the remaining risk has moved out of reading and into execution, the deferred economic review, and the independent external audit. Another reading round is unlikely to earn its keep.

This remains an internal review and closes none of the production-assurance gates.

Earlier roundRemediation re-check→