Banked: Cycle 219 (Pendle Boros, Cantina $500K bounty, public repo boros-core-public HEAD 78403bf). WALK-CLEAN target. Class: funding-rate / interest-rate-swap perp-like venue with margin accounts (recurring target class - Boros, perp DEXes, Synthetix-style funding, GMX-style, any "trade the funding rate" primitive). Relationship: unifies the cycle206 "measure-the-sign rounding drift" lens and the cycle199 derived-tenor maturity-boundary lens. Both seams are closed by the SAME design property: a single keeper-published, monotone, shared index that drives every value-moving gate.
Boros settles funding per-account against a shared floatingIndex published by an onlyKeeper FIndexOracle.updateFloatingIndex. The per-user accrual is
payment = signedSize.mulFloor(curFloatingIndex - lastFloatingIndex) // round toward -infinity
fee = |signedSize|.mulUp(curFeeIndex - lastFeeIndex) // round up
Maturity is _isMatured = latestFTime >= k_maturity, where latestFTime is the FIndex time (keeper clock), NOT block.timestamp.
mulFloor rounds the signed payment toward -infinity for BOTH signs. A long (+S) and an equal-and-opposite short (-S) over the same Δindex floor to a sum in [-1, 0] wei -> the SYSTEM retains the dust, never pays out net. (Verified: testFuzz_longShortConservation, 2000 runs.)testFuzz_splitDoesNotFarm.)rawDivUp (margin up, against user), position value via rawDivFloor (value down, against user).
=> On funding venues, the rounding-farm hunt resolves to PASS whenever (a) accrual is size * Δsharedindex floored toward -inf, AND (b) the user does not control the index delta. Confirm those two facts, then DON'T spend hours fuzzing for a farmable drift.Every value-moving gate (trade open, market entry, settlement clamp, timeToMat) funnels through latestFTime (the keeper FIndex time), not block.timestamp. There is NO second independent block.timestamp inequality to disagree with the FTime gate. This is exactly the cycle199 "derived quantity routed through one monotone source" form that is immune to the Midnight solvent-at-fill / liquidatable-next-block primitive. The index is clamped at maturity (timestamp >= maturity -> (maturity, maturity)), so funding cannot advance past maturity either.
block.timestamp-driven accrual (user can time it)? If keeper-published shared index -> rounding-farm and maturity-boundary are BOTH likely closed; confirm the two facts above in ~15 min, then pivot.size * Δindex floored toward -inf (or any single rounding that is symmetric-conservative across long/short). Fuzz long+short conservation if unsure (faithful-copy the kernel, don't fork the world).FIndexOracle.updateFloatingIndex = onlyKeeper; mark rate = trusted oracle. A malicious keeper/oracle is OUT OF SCOPE on this and most funding venues. Do not model keeper dishonesty; model what an untrusted user reaches through signed intents given an honest keeper.When the value-moving core is a small pure-math kernel (here PaymentLib.calcSettlement + PMath rawDivFloor/mulUp), copy the kernel VERBATIM into a standalone Foundry project and fuzz the conservation invariant directly. This avoids wiring a 30-file type/storage graph or a mainnet fork, and the proof is just as load-bearing because the copied bytes are the production logic. Confirmed faster + sufficient on Boros (5 tests, 2000 runs, minutes).
value + cash >= margin and accrual is size * Δindex.On a funding-rate margin venue, first find the funding clock. If it is a keeper-published shared index whose delta the user cannot control, and accrual is a signed-size-times-index-delta floored toward -infinity with margin rounded up, then the cycle206 rounding-farm AND the cycle199 maturity-boundary seams are BOTH structurally closed - confirm the two facts in minutes and reallocate the hunt to cross-module composition and callback actor-binding. A single monotone shared clock is the design that immunizes a perp-like venue against the two most common fixed-maturity-margin seams at once.