---
title: "Security Review 2026-09"
description: "Public subset of the September 2026 pre-launch security review: scope, method, findings and dispositions for the open-source components."
audience: both
type: reference
status: live
lang: en
updated: "2026-09-16"
publish: true
---
# Security review, September 2026

Internal pre-launch review, closed 2026-09-16. This page publishes the subset that
covers the open-source components. Findings are published under the
[disclosure policy](/docs/3-7-bug-bounty): a finding appears once its fix is
deployed to every chain running the affected code.

## 1. Scope

Audited heads, frozen 2026-09-15:

| Component | Repository | Audited head |
|---|---|---|
| AIMM pools, Admin, oracles, periphery, deploy scripts | `dex-evm` | `149fb16e3c` |
| Shared access control, quorum, upgrade gate, timelock | `shared` | `183ee41264` |
| TypeScript SDK: ABIs, router, transport | `sdk` | `99bc688fb9` |
| Web application | `front` | `ea7150fb` |
| Rust pricing mirror | `core` | `0d1990f7dd` |

Remediation heads, merged 2026-09-16: `dex-evm aca89e4e5a`, `shared 957d3b0939`,
`sdk a95aabe`, `front 8557e6c7`, `core 8ef3ef66b9`. Every fix commit cited below is an
ancestor of its component head, checked mechanically.

Out of scope publicly: the back-end services, the keepers, the price feed producer and
the operational environment. Those were reviewed in the same campaign and are disclosed
to auditors under non-disclosure. Residual pre-launch ceremony items are tracked
privately until launch.

## 2. Method

The review is a multi-model adversarial process rather than a single pass. Several
frontier model families are rotated across finding, adversarial refutation, debate,
cross-validation, and test and fuzz generation, because different pretraining produces
different blind spots and that is the only axis that attacks blind spots shared by every
prompt we can write. It is refute-first: every candidate goes to two independent
reviewers instructed to refute and defaulting to refuted, the party that disagrees
carries the burden, and a split goes to a third reviewer who has seen neither
refutation. Severity is graded at the shipping configuration, never at a hypothetical
one, with exploit difficulty and blast radius stated beside the grade. Each cited fix
commit is asserted to be an ancestor of the published component head, so the fix claimed
here is the code that shipped. The full method is published in the audit repository.

## 3. Summary

52 rows in the public scope. No critical severity.

| Severity | Fixed | Accepted | Refuted | Closed to a launch step | Total |
|---|---|---|---|---|---|
| High | 4 | 0 | 0 | 0 | 4 |
| Medium | 6 | 0 | 0 | 0 | 6 |
| Low | 18 | 3 | 0 | 0 | 21 |
| Informational | 8 | 11 | 1 | 1 | 21 |
| Total | 36 | 14 | 1 | 1 | 52 |

"Accepted" means the behaviour is the intended design and carries an operational
control instead of a code change; the reasoning is in section 6.

## 4. Findings

### A-1500 High: testnet-only deployment paths ran unguarded on a production class
**Description.** `dex-evm/script/PoolDeploy.s.sol:341`. the pool deployment script built and funded a testnet faucet with no check on the deployment class, sizing the transfer at four times the seed itself; a fourth-generation oracle script likewise carried no production refusal while resolving the deterministic address rows reserved for the fifth.
**Impact.** real seed capital in a production deployer wallet would be moved into a permissionless faucet and withdrawable by anyone, with no budget check or operator warning.
**Remediation.** every testnet-only path is now confined to non-production classes, the legacy oracle deployment refuses a production chain, and script tests assert the refusals. Fixed in `633621d`.

### A-1501 Medium: launch parameter scaffold breached the minimum-fee floor
**Description.** `dex-evm/deployments/bnb-risk-params.json:84`. the scaffold manifest set minimum fees below twice the push threshold on two legs and a two-hour staleness time-to-live on stable assets, while every gate that would catch it is inert before listing.
**Impact.** a leg whose minimum fee sits below twice the push threshold gives up value to informed flow on each quiet-market approach to that threshold, and a long stale window prices a stale mark below its true risk.
**Remediation.** the parameter refit now sets the minimum fee from the push threshold in the same change that sets it, resolves the stale time-to-live, and populates the roster so the floor assertion executes at keeper boot. Fixed in `8920758`.

### A-1503 Low: deployment library trusted operator input
**Description.** `dex-evm/script/lib/ChainParams.sol:168`. the chain assertion compared a value against a manifest selected by that same value, parsed parameters were cast to 16 bits before their ceiling checks, and per-index signer overrides applied on production chains.
**Impact.** a stale rehearsal override could place a publicly known key into a production signing quorum, and a truncated deviation or time-to-live silently configures a feed outside its intended bounds.
**Remediation.** an explicitly supplied chain identifier must now equal the running chain, every parsed value is range-checked before the cast, and signer overrides are refused on production chains. Fixed in `a8897e7` and `5539d9a`.

### A-1504 Low: contract CI checked out the shared repository unpinned
**Description.** `dex-evm/.github/workflows/test.yml:36`. the workflow checked out the sibling shared repository at its default branch, so a green run did not identify the shared commit it built against.
**Impact.** contract CI results were not reproducible and could not serve as release or audit evidence. No on-chain consequence.
**Remediation.** the sibling checkout is pinned to a reference carried in the repository, which must move in the same commit as any call-site change. Fixed in `7398ab1` and `8b67c89`.

### A-1505 Low: operator restore scripts proved the wrong interleaving and aborted on one entry
**Description.** `dex-evm/script/ArcRiskRestore.s.sol:688`. the swing-cap proof paired the target dispersion with live values and hardcoded the cap instead of deriving it, and batch cancel and widen runs reverted whole when a single entry was empty or still maturing.
**Impact.** a risk restore could abort mid-procedure and leave the fleet partially reconfigured, and the safety proof did not cover the state that actually exists on chain. No funds at risk.
**Remediation.** the proof now takes the worst case of target and live values and derives the cap from the pricing constant; cancels skip empty lanes and widen batches continue past entries that are not yet ready. Fixed in `bf1caca`.

### A-1510 Low: a kappa raise voided any queued risk operation
**Description.** `dex-evm/src/Admin.sol:552`. the steward-callable coverage raise unconditionally dropped any live queued risk operation, including one whose execution could not undo the raise.
**Impact.** a lower-privileged role could hold off owner-initiated risk changes indefinitely, and the only owner remedy also removes the automated risk keeper from service.
**Remediation.** the queued operation is now dropped only when its payload would lower coverage below the new value; a regression test covers the non-conflicting case. Fixed in `41e10bc`.

### A-1511 Low: the oracle beacon upgrade sits at the listing delay with a nominal validation
**Description.** `dex-evm/src/oracles/OracleBeacon.sol:88`. The beacon upgrade delay is the one-day listing tier, and its validation checks only that the candidate implementation reports the expected access-control address and a storage version at least as high as the live one, failing open when an implementation exposes none. Both oracle tiers shared one access-control owner.
**Impact.** Bounded by the one-day delay and guardian veto. The residual is the correlated window across two tiers rather than the validation logic, since the owner is already trusted for implementation code.
**Remediation.** Accepted, ratified by the owner. The reference access control takes a distinct owner at handover, removing the single-key overlap, and the guardian runbook covers the upgrade-request event on both tiers.

### A-1512 Low: a guardian halt on a transferable pool carried no release delay
**Description.** `dex-evm/src/Admin.sol:330`. the halt entry point recorded the guardian timestamp only when the caller was a pool administrator, and the unhalt branch never cleared a stored one, so the release comparison passed immediately on an unstamped halt.
**Impact.** a guardian halt on a transferable pool could be lifted in the same block by a newly appointed administrator, defeating the minimum release delay. Official pools are not transferable and were unaffected.
**Remediation.** the timestamp is now recorded whenever the guardian bit is set and deleted when it is cleared, on every pool. Fixed in `56020da`.

### A-1517 Medium: releasing a darkened price lane loosened its band permanently
**Description.** `dex-evm/src/oracles/ExternalOracleV5.sol:450`. a lane darkened by a deviation breach could only be released by raising its sigma floor, and no code path ever lowered that floor again, so the released lane kept a per-push band ten times its deviation ceiling; self-healing also discarded the refused push's sigma.
**Impact.** one relay outage across a moderate move can make a leg unavailable for hours to days and refuse deposits and cross-asset exits pool-wide, and the operator remedy leaves that lane with a permanently loosened per-push band. No direct loss of funds.
**Remediation.** the sigma floor is now a tighten-only field on the feed update path, so a release is widen, execute, then tighten back; self-heal accepts a raise-only sigma from a refused push under the same ceiling. Fixed in `a8c5463` and `ac57e66`.

### A-1521 Low: fee-free liquidity flows were a lower-cost trading channel
**Description.** `dex-evm/src/libraries/PoolLiquidity.sol:414`. same-asset exits and deposits settled at the coverage ratio with no fee, spread or staleness premium, so four related paths let a liquidity provider trade through the liquidity surface more cheaply than through the swap surface: exiting before a predictable push, straddling a skew move, converting a deposit into a cross-asset exit, and following a liability swap with a same-asset exit.
**Impact.** passive liquidity providers subsidise the timing of active ones on each predictable mark move, and three conversion paths bypass the swap fee schedule. A few basis points per event at launch caps.
**Remediation.** an explicit exit toll was implemented in the liquidity library with the supporting pricing term, covered by a dedicated exit-fee suite and updated coverage proofs, plus escrow for the case where the book cannot hold the fee. Fixed in `e464872` and `a9fd0e2`.

### A-1522 Low: the internal-mode depeg breaker never compared against the peg
**Description.** `dex-evm/src/libraries/PoolIOLib.sol:368`. for a leg priced at a constant peg, the guard tested only that the primary and reference feeds agreed with each other. A genuine depeg moves both together, so the breaker would never fire.
**Impact.** such a leg could be sold into the pool at par throughout a real depeg, moving the full depeg magnitude from pool liquidity to the counterparty until the coverage wall binds. Latent at launch, since no leg is configured this way.
**Remediation.** the internal branch now also reverts when the raw price deviates from the unit peg by more than the reference band, with a test asserting the revert when both feeds report 0.95. Fixed in `ff2b49c`.

### A-1523 Low: one unusable feed suspends liquidity paths pool-wide
**Description.** `dex-evm/src/libraries/PoolSolvency.sol:65`. The solvency routine skips only legs whose reserves and liabilities are both zero and fails on the first leg with an unusable mark, after which deposits, donations, cross-asset exits, liability swaps and hook credit all refuse across the pool.
**Impact.** Liquidity-facing paths for a whole pool are suspended for as long as one of its feeds is unusable. Swaps on the same asset and same-asset exits continue at a clamped coverage ratio, so the consequence is availability, not solvency.
**Remediation.** Accepted with no code change: the fail-closed posture is the intended safety property, since every leg must be gateable. The per-feed time-to-live is documented as a pool-wide service level for liquidity paths and reflected in keeper paging thresholds.

### A-1524 Low: a base-token migration re-anchored spokes without re-denomination
**Description.** `dex-evm/src/libraries/PoolConfig.sol:808`. the migration validated the incoming base mark against the depeg ceiling but moved every anchor-quoted spoke onto the new base without re-denominating its attested price, and the reference band could not detect the gap because both feeds share the stale unit.
**Impact.** between the base change and the paired anchor update, spokes are mispriced by up to twice the depeg ceiling, allowing value to be taken from the pool until the coverage wall limits further trades.
**Remediation.** the migration now reverts unless every anchor-quoted spoke is halted at the time of the change, so the operator halts before and unhalts after the anchor update lands; the bound is stated explicitly in code. Fixed in `08b9981`.

### A-1525 Low: the reference tier is a second hard liveness dependency
**Description.** `dex-evm/src/libraries/PoolIOLib.sol:365`. The price band gate fails closed whenever a reference band is armed, and the launch configuration arms one on every spoke asset, so a stale reference mark cannot be evaluated and the swap reverts.
**Impact.** A reference-tier outage makes all banded spoke swaps unavailable for its duration. Same-asset liquidity exits continue and no position can be mispriced or liquidated as a result.
**Remediation.** Accepted as designed: a stale reference cannot bound a walked primary mark, and a degrade-to-wider-spread fallback would reopen the path the band exists to close. Mitigation is operational, with alarms when a reference lane's age exceeds half its time-to-live.

### A-1528 Low: a force-clear path was missing the flash-loan guard
**Description.** `dex-evm/src/Pool.sol:756`. the administrative hook force-clear was the only ledger-moving hook path that did not assert the absence of an active flash loan, and the write-down it performs prices the cut against reserves that exclude the balance out on loan.
**Impact.** executed inside a flash-loan callback, the coverage ratio reads low, the face cut is oversized, and the depressed last-good coverage persists after the loan settles. Bounded to one hooked leg and reachable only by the pool administrator.
**Remediation.** the guard is now the first statement of the force-clear, with a test asserting the revert inside a flash-loan callback, and a contradictory comment was removed. Fixed in `5188023`.

### A-1529 Low: one wei of venue rounding stopped yield-hook rebalancing
**Description.** `dex-evm/src/hooks/YieldHook.sol:393`. the harvest booked any shortfall as a loss, including the one-wei floor that vault share arithmetic leaves behind, and that write-down path reverts whenever any armed leg has an unusable mark.
**Impact.** routine rounding turned an unrelated feed outage into a full stop on rebalancing for the affected leg, and repeatedly booked sub-dust write-downs that permanently reduce the index.
**Remediation.** the harvest now absorbs venue rounding below a dust threshold instead of booking it, with the same treatment on the lending-pool adapter and new adapter tests against a mock venue. Fixed in `df0367d`.

### A-1530 Low: over-weight Merkle entries would disable the final claimants
**Description.** `dex-evm/src/periphery/WombexClaim.sol:141`. the claim reverted whenever cumulative weight exceeded one unit, no code enforced that a distribution tree summed to at most one unit, and the ratified design has an immutable root with no rescue path or claim expiry.
**Impact.** with a ceiling-rounded tree, one or more final claimants lose their entire allocation and the funds stay in the contract with no recovery path.
**Remediation.** the claim now clamps the weight to the remaining room after proof verification, so a payout can only shrink and the per-bucket invariant still holds; the tree builder floors weights and asserts the sum before deployment. Fixed in `1ff4d9f`.

### A-1547 Medium: a server-authored output floor was accepted at any tolerance
**Description.** `sdk/src/router/index.ts:104`. the router verified only that the minimum output was internally consistent with the quoted output and the tolerance, and never compared either against the caller's own slippage setting, while the tolerance itself was clamped only at 99.9 percent.
**Impact.** a user routed through a compromised quoting service could have essentially the whole value of a swap taken in one transaction, with the on-chain slippage check providing no meaningful protection.
**Remediation.** the floor check now receives the request slippage and requires the tolerance to match it in relative mode or stay under the caller-supplied maximum in spread mode; the router requires that maximum whenever server floors are present and throws when it is exceeded. Fixed in `58eb51c`.

### A-1550 Low: chained two-hop legs pulled the intermediate token from the wrong party
**Description.** `sdk/src/router/index.ts:436`. the self-directed assertion ran only on the unwrap path, while a chained route delivers hop one's output to the recipient and then has hop two pull that same token from the sender.
**Impact.** an integrator calling the SDK directly with a recipient different from the sender either sees a revert or has an unrelated sender balance consumed. The shipped interface pins recipient to sender, so it was inert there. No pool invariant is broken.
**Remediation.** validation now detects chaining, defined as a leg whose input token equals an earlier leg's output, and applies the self-directed assertion in that case, with a regression test. Fixed in `e454eeb`.

### A-1555 Low: SDK builds sourced pinned ABIs from a live API
**Description.** `sdk/scripts/fetch-abis.ts:41`. the build fetched contract ABIs from the production API and verified them against a committed lock file, with no committed artifact to fall back on, so a mismatch stopped the build outright. Three consecutive CI runs failed this way.
**Impact.** during exactly the window in which a hotfix is most likely to be needed, every SDK and web build can fail, blocking releases until the service and the pin are reconciled.
**Remediation.** the pinned ABI JSON is now committed alongside the lock file and read first, and the remote fetch was demoted to a drift check that logs rather than throws. The lock file remains the trust anchor. Fixed in `faade8d` and `2185748`.

### A-1584 High: a multi-part liquidity deposit debited the full typed amount on the first leg
**Description.** `front/src/components/features/swap/SwapForm.tsx:1121`. the deposit call builder assigned the entire exact input to the first part and sized every later part from a floating-point fill, because it hand-mapped the plan instead of using the shared plan-to-legs conversion.
**Impact.** users are debited more than the amount they entered, the first leg executes with a protection floor that does not match its size and is exposed to sandwiching, and chained hops can revert or strand intermediate balances.
**Remediation.** the exact input is now split across parts by their fractions with the remainder on the last part, later fills take their input from the previous leg's minimum output and are marked chained, and a test asserts the sum. Fixed in `c24b397` and `828dc37`.

### A-1585 High: token decimals were keyed by symbol rather than by chain
**Description.** `front/src/config/testnet-tokens.ts:205`. the metadata table pinned USDC and EURC at six decimals on every chain, while the bridged USDC on BSC carries eighteen. Eleven call sites read the listing decimals, including the encoded swap amount and the balance gate.
**Impact.** swap amounts, balance checks and displayed figures on that chain are wrong by twelve orders of magnitude, and liquidity deposits carry no effective protection floor.
**Remediation.** listing decimals are resolved per chain rather than per symbol, the swap form consumes the chain-resolved value, and a configuration test covers the resolution. Fixed in `a2d8219`.

### A-1586 Low: test harnesses reimplemented the curve encoder
**Description.** `front/scripts/lib/livePools.ts:168`. two harnesses hardcoded the curve median sentinel and wrote every boundary unconditionally instead of importing the canonical encoder, and those harnesses are the only parity gate between the TypeScript and Rust curve implementations.
**Impact.** the parity check could produce false results for any curve whose centre is not the hardcoded sentinel, removing the assurance that the two implementations agree.
**Remediation.** both copies were deleted and the harnesses import the SDK encoder, so the parity battery exercises the production code path. Fixed in `c2a7783`.

### A-1587 High: failed batched chain reads shrank the safety roster and defaulted permissive
**Description.** `front/src/hooks/useSafetyControl.ts:135`. failed sub-reads inside a batched read were treated as absent rather than unknown, a fetch hook retained previous data across a key change so index-aligned consumers paired stale results with a new roster, and reference liveness was inferred from rows being present.
**Impact.** an operator can believe a fleet-wide safety action covered every pool when it silently omitted those whose reads failed, can be shown parameters belonging to a different pool, and can see gates reported as permissive purely because a read failed.
**Remediation.** the fetch hook returns data only when its stored dependency key matches, the safety hook exposes an incomplete state and disables sending on any failed sub-read or count mismatch, unread feeds render as failures, and the portfolio read fails closed. Fixed in `2c8892a`.

### A-1588 Low: halt labels and safety copy were stale after the anchor-bit split
**Description.** `front/src/components/features/admin/useSafetyHistory.ts:249`. any non-guardian bit was labelled a risk bit, which wrongly included the anchor halt bit; attribution read the source bit before the sender; and the safety copy understated what a halt blocks and overstated what an unhalt clears.
**Impact.** operators can misidentify the source of a halt and attempt an ineffective remedy during an incident, extending the outage. No incorrect state is written on chain.
**Remediation.** a three-way label distinguishes anchor, guardian and risk bits, attribution checks the sender first, the copy was corrected, and a label test was added. Fixed in `b8b6405` and `4a6041d`.

### A-1589 Low: oracle push decoding dropped lanes missing from the build-time map
**Description.** `front/src/components/features/oracle/oracle.tsx:244`. a lane whose index was absent from the build-time metadata map decoded to nothing and was skipped, while the push was still rendered as verified.
**Impact.** operators see an incomplete lane list presented as a complete, verified push, which can mask a newly registered feed that is not being monitored. Display only.
**Remediation.** the diff now counts records that failed to decode and renders an explicit unregistered-lane indicator on the push row. Fixed in `1b384ff`.

### A-1590 Medium: a broadcast transaction could be reported as cancelled
**Description.** `front/src/lib/walletCalls.ts:208`. the check that decides whether a transaction was broadcast despite an apparent wallet rejection polled the account nonce only at zero and 1.2 seconds, so any slower response turned a live transaction into a confident assurance that nothing was sent.
**Impact.** a user can be told no transaction exists while one is in flight, leading to a duplicate submission and an unintended second fill, with the matching gas and market exposure.
**Remediation.** the nonce poll now runs a ladder at 0, 1.2, 3 and 6 seconds, and the next send compares the pending nonce against the one recorded before the rejection and warns when it has advanced. Fixed in `93383ae`.

### A-1591 Low: a partially successful batch emitted no confirmation for the calls that mined
**Description.** `front/src/lib/wallet.tsx:477`. the receipt loop threw on the first reverted receipt and emitted its confirmation only after a clean pass, but a wallet batch is not atomic, so earlier successful calls produced no event.
**Impact.** the interface misrepresents on-chain state after a partial batch, leading users to repeat calls that already executed. No loss beyond the redundant gas.
**Remediation.** a confirmation is emitted for each successful receipt before throwing on the reverted one, and the balance refresh survives a duplicated mount. Fixed in `710cbbf` and `1d7b649`.

### A-1592 Low: multi-hop approval rows were labelled with the first hop
**Description.** `front/src/components/shared/TxSteps.tsx:79`. the transaction overlay computed one target from the first hop and rendered every approval row against it, while a cross-core route emits one approval per distinct token-and-pool pair.
**Impact.** users signing multi-hop approvals see an incorrect token, pool and amount for every row after the first, which undermines their ability to verify what they authorise. The transactions themselves are correct.
**Remediation.** the overlay tracks an approval counter and pairs the k-th row with the k-th distinct token-and-pool pair along the route. Fixed in `0193040` and `6e9238c`.

### A-1594 Medium: the web CI pinned a toolchain version that does not exist
**Description.** `front/.github/workflows/ci.yml:44`. the workflow pinned a toolchain release that was never published, so four consecutive runs on the main branch failed at setup and every downstream step was skipped rather than executed.
**Impact.** every automated quality gate on the main branch was inert since the pin moved, so regressions in chain reads, typing, linting and bundle composition could reach the branch undetected.
**Remediation.** the pin was moved to the published release so the setup step resolves and the gates run again. Enforcing that a red pipeline blocks merges stays a branch-protection setting. Fixed in `fe4fdcc`.

### A-1595 Medium: two routes rendered before the invite and disclaimer gate
**Description.** `front/src/App.tsx:318`. the early returns for the chart and swap-form routes were evaluated before the disclaimer condition and neither route was listed as public, while the wallet reconnects silently.
**Impact.** trading was possible without invite authorisation or a recorded risk acceptance, undermining both the launch-phase access control and the acceptance record.
**Remediation.** the disclaimer block was moved above both early returns, so the routes are gated like every other non-public route, with a test asserting the gate renders. Fixed in `5a56106`.

### Informational rows

- A-1506: launch manifest notes contradicted the shipped design. Fixed in `279ec8e`.
- A-1508: pool-level solvency stays unarmed after a beacon swap until an explicit governance backfill. Accepted.
- A-1513: a foreign pool seat can widen its own fences and write inside them in one transaction. Accepted, documented for liquidity providers.
- A-1514: signer revocation refuses to drop below threshold; at the threshold the response is a per-feed pause. Accepted.
- A-1515: the guardian can seal a sentinel pool mid-deployment. Accepted.
- A-1516: one operation type has no dispatch arm and its request reverts. Accepted.
- A-1519: feed registration has no time-to-live ceiling below the 16-bit maximum. Accepted.
- A-1520: a claim that the maximum source age lets a captured push refresh an expired lane. Refuted, not upheld on the audited head.
- A-1526: cross-asset withdrawals and interior hops do not check the swap-enabled bit. Accepted.
- A-1527: the 16-bit spread saturates above 6.55 percent, discarding further premium. Accepted.
- A-1548: unit formatting was wrong for zero decimals and for negative values. Fixed in `8633f8e`, `f4ef785`, `58d301f`.
- A-1549: approvals carry no zero-first reset for tokens that require one. Accepted.
- A-1551: a chain registry labelled the wrapped native token as native. Fixed in `1bc8d97`.
- A-1552: no venue record for chain 56 and the file is hand-maintained. Closed to a launch checklist step.
- A-1553: the SDK readme still described a deleted off-chain pricer. Fixed in `154904a`.
- A-1554: an SDK swing-cap constant disagreed with the chain and the Rust mirror. Fixed in `ec109f1`.
- A-1556: the Keccak implementation panicked on inputs ending exactly on the rate boundary. Fixed in `8ef3ef6`.
- A-1593: two gas-reserve formulas can disagree. Accepted.
- A-1596: a build plugin referenced an undeclared variable. Fixed in `e2694a8`, `92885d3`.
- A-1597: a merged asset metric took the first non-null value instead of weighting by value locked. Fixed in `0fcd1b9`.
- A-1598: a candle store evicted the newest entries past its cap. Accepted.

## 5. Refuted

One public row was refuted and closed without a code change. A-1520 held that the
six-hour maximum source age exceeds a lane's time-to-live, so a captured push could
refresh an expired lane. Re-read against the audited head, the lane decoder rejects the
captured observation before that path is reached, so the claim does not hold.

From the earlier campaign, the claim that an incomplete legacy roster left a pool
unrecoverable was refuted as unreachable: an asset that is listed but not rostered
cannot exist. The misleading comment that motivated the claim was corrected.

## 6. Accepted design positions

Fourteen rows were accepted rather than fixed: the behaviour is the intended design and
carries an operational control instead of a code change. The substantive ones are stated
in full above (A-1511, A-1523, A-1525). The rest are informational and listed in section
4: a foreign pool seat can move inside its own fences in one transaction; signer
revocation refuses to drop below threshold, so the response at the threshold is a
per-feed pause; the 16-bit spread saturates above 6.55 percent; cross-asset withdrawals
do not re-check the swap-enabled bit. De-risking actions such as a coverage raise and a
steward revocation stay instant by decision, and that asymmetry against timelocked
upgrades is deliberate.

## 7. Prior campaign

The September review closed a longer campaign that ran from 2026-09-02. The bundles
below are the ones whose surface is open source; each is a single invariant with a
single fix shape.

| Bundle | Surface | What was wrong | What closed it | Status |
|---|---|---|---|---|
| B01+B19+B47 | Pricing mirror parity across Solidity, Rust and the research sim | Vega was priced on the wrong scale, the sim ran against a stale constant set and a dead parity path, and the Rust sell leg settled in the wrong units | One stated scale in every mirror, research constants pinned to the Solidity source, gross rescaled into the hub units before settle | Closed |
| B03+B27+B29+B31+B32+B64 | Oracle wire and sigma: push semantics, replay, skew, confidence, heal bound, quorum read | A day-aliased push could refresh a stalled slot, bounded future observations read fail-closed, the sigma floor dropped on price-only pushes, and the heal bound was unbounded at long gaps | One lane decoder, sigma floored at the realised move on every push shape and capped against the deviation ceiling, the quarantine bit and re-entry path deleted | Closed on chain; producer wire migration tracked separately |
| B08 | Authority: official pools, sentinel, beacon and sweep | An ownership rotation de-branded every official pool, asset halts enumerated unaffiliated clones, and an off-factory clone could present as official | A previous-owner shield at least as long as the pool delay, official-only enumeration, and initialisation anchored to the beacon and the factory | Closed |
| B09 | Governance halt and anchor bits | A collapse halt used the guardian bit, so an unhalt relisted a re-anchored leg with no re-attestation | An anchor-owned halt latch on chain, in the SDK and in the pricing mirror; the interface sends the settable mask only | Closed |
| B10+B48 | Hook net asset value, invested ledger and binding | A hook refusing recall made replace and clear unavailable, a halted leg blocked evacuation, and the credit cap mixed token and face units | A force-clear lane, evacuation kept live on a halted leg, credit and fees booked at coverage, and binding that verifies pool and access control | Closed to a launch step |
| B11+B39+B62 | Router floor semantics | The SDK authored its own protection floors and compounded them across hops, and untracked rebase slack could become protocol fees | Server floors only, allocated per part and refused without one, with the liquidity floor re-checked after an exotic leg | Closed |
| B12 | SDK transport and ABI encoding | A public endpoint ring with no chain attestation, and an encoder that accepted malformed input | Each endpoint attests to its chain and is evicted once on mismatch; fixed-size bytes, arrays, addresses and integer bounds are checked | Closed |
| B24+B57 | Liability-swap ledger and entry seal | Coverage was applied twice on the out leg, over-minting, and the out-leg mint bypassed the depositor allowlist and the seal | The out leg re-denominated to face at coverage and mirrored in the SDK; the mint now clears the allowlist and the seal | Closed |
| B25+B37 | Queued risk-parameter lanes | A queued absolute risk update had no request-time snapshot, and the first fix made de-risking depend on a privileged cancel | An instant tighten is refused while a risk operation waits, a coverage raise voids only a stale key, and the tightening caller may cancel | Closed |
| B26 | Access-control bootstrap and quorum guard | Role bootstrap was un-gated after arming, and the quorum check was fail-open at a threshold of zero | Post-arm bootstrap refused, arming requires the factory, and zero thresholds, empty signature sets and unseeded signer sets are rejected | Closed |
| B28+B55+B56 | Hook credit at par versus at coverage | Hook yield booked at par while donations booked at coverage, face-at-coverage divided by zero, and harvest reverted on any unusable unrelated mark | Yield and fees booked at face, five division sites refuse zero coverage, and an unusable mark degrades fail-closed | Closed |
| B30 | Legacy roster backfill | Claimed that an incomplete legacy roster left a pool unrecoverable | Refuted as unreachable: a listed but unrostered asset cannot exist. The misleading comment was corrected | Closed |
| B33 | Hook cancel authority | The cancel authority was the protocol guardian rather than the pool seat, so a keeper could reach a third-party pool's slot | Cancels are seat-routed on third-party pools and the hook pool reference is immutable | Closed |
| B34 | Invariant and generic test pins | Two invariant pins were absent, the fuzz handler skipped four paths, reverts were untyped, and the generic matrix covered 3 of 18 selectors | Pins landed, the handler widened, reverts typed, a skipped placeholder deleted and the full matrix restored | Closed |
| B38 | Claim periphery | The distribution tree accepted a zero root and duplicate legs | Zero roots and duplicate legs refused; the anti-just-in-time window kept | Closed |
| B40 | Comment and documentation overclaim | The swing cap and several parameter comments cited superseded numbers, and the beacon layout comment overclaimed upgrade safety | Numbers made symbolic or corrected; stale comments deleted along with the levers they described | Closed |
| B43 | Published ABI and event surface | The SDK and back-end pinned ABIs lagged the contracts: a deleted selector, missing factory selectors and a hand-copied oracle ABI | ABIs generated in the contract repository and consumed by path, the oracle interface published, and hashes locked | Closed |
| B54+B61 | Solvency degrade semantics | One unusable non-zero leg froze the pool, the par degrade that followed mispriced coverage, and out-of-band marks fed coverage unbounded | Fail-closed degrade, exits paying the lesser of one and last-good coverage, and out-of-band marks bounded at the reference band edge | Closed |
| B59 | Build pins and artifact freshness | An optimiser-run mismatch broke parity between the two contract repositories, storage layouts were unpinned and slot assertions were tautological | Runs restored with a CI parity gate, storage frames pinned, and deterministic library pins enforced | Closed |
| B60 | Signer and reference governance lifecycle | Grant and revoke were untested, and revoke could leave the threshold above the signer count | Nine lifecycle tests; revoking below threshold is refused on both tiers | Closed |
| B65 | Mechanism ratification | The tip shipped a paused three-tier ladder and a beacon delay the owner had not ratified | The owner ratified an upgrade-only beacon with the oracle implementation at the listing delay, and the repoint lane was deleted | Closed |

## 8. Coverage limits

This review is internal. It is not an independent opinion and does not substitute for
one; third-party audit is pending and reports will be linked from the
[Security Overview](/docs/3-overview) when they land. The public subset here
omits the back-end services, the keepers, the price feed producer and the operational
environment, which were reviewed under the same method and disclosed to auditors under
non-disclosure. Proof-of-concept exploits are never committed in any repository, and
implementation-specific checklists are held: they are a map, not a method.

Findings against deployed contracts go to **security@btr.markets**. Please do not open a
public issue for anything exploitable. We confirm receipt, say whether the finding is
already in the private ledger, and tell you when the fix is deployed; once it is, the
finding is published with attribution unless you ask otherwise. See
[Bug Bounty](/docs/3-7-bug-bounty) for scope, rewards and safe harbour.
