Inverse-Price Floor-to-Zero
- Name: inverse-price floor-to-zero
- Category: exploit pattern / inverse-price arithmetic failure / rounding-to-zero solvency collapse / collateral-integration bug
- Summary: Some lending systems do not consume a collateral mark directly. They invert it first, often as a fixed-point
exchangeRate = K / price, and then feed that inverse value into loan-to-value or solvency math. If the upstream price can be pushed high enough, integer division floors the inverse down to0. At that point the system stops sayingthis collateral is very expensiveand starts sayingthis collateral contributes zero debt risk, which can disable borrow limits, liquidation thresholds, or health-factor checks outright. The sharp lesson is that a truthful but attacker-inflated price can become strictly worse after inversion: not merely overpriced collateral, but literal zero-risk collateral. - What it is:
- A protocol prices collateral through an inverse transform rather than consuming the raw mark directly.
- The inverse is computed with integer division against a fixed numerator such as
1e18,1e36, or another normalization constant. - The protocol assumes the denominator cannot become large enough for the inverse to round to zero.
- An attacker finds a way to inflate the upstream price or exchange rate honestly enough that the inverse transform underflows to
0by flooring. - Downstream solvency math treats
0as a valid low-risk state instead of an impossible or revert-worthy state.
- Typical preconditions:
- The protocol stores or consumes
1 / price,1 / exchangeRate, or another reciprocal quantity inside borrow-side math. - The upstream price surface can be pushed very high through donation, empty-vault bootstrap, wrapper exchange-rate inflation, or a configuration mistake.
- A borrow, cache-refresh, admin-set, or collateral-update path is allowed to publish the reciprocal as canonical local truth before the final solvency guard runs.
- No invariant enforces
inversePrice > 0before collateral admission, borrow, or health-factor checks proceed. - The zero value is interpreted as
no collateral cost,no LTV contribution, or another algebraically safe-looking state rather than a fatal error. - A downstream borrow, withdrawal, or leverage path can immediately monetize the false-solvency state.
- The protocol stores or consumes
- Common vulnerable surfaces:
- lending markets that invert ERC-4626 or wrapper share prices into
exchangeRateform before solvency checks - collateral adapters that normalize prices as
K / oraclePricewithout upper-bound checks on the oracle answer - borrow engines that accept
0as a syntactically valid exchange rate, collateral factor input, or debt-scaling term - fresh collateral listings where the upstream share price can be dominated cheaply before any smoothing or seeding exists
- publication paths that cache or store the reciprocal during
borrow(), collateral updates, or market syncs and then reuse that stored value in later modifiers or helper checks - math paths whose comments reason in reals while the implementation uses floor-rounded integers
- lending markets that invert ERC-4626 or wrapper share prices into
- Exploit sequence:
- The attacker gets control over a collateral mark that the protocol will later invert.
- They inflate that mark high enough that
K / mark < 1in integer arithmetic. - The protocol stores or reads the inverse value as
0after floor division. - Borrow-side math interprets the position as extremely safe or contributes zero debt risk.
- The attacker posts tiny or dust collateral and borrows real assets against the false-solvency state.
- The protocol is left with bad debt once the impossible
inverse = 0state is corrected or the collateral proves unrealizable.
- Variants:
- Donation-amplified inverse collapse — a donation-sensitive vault or wrapper prints a huge but mechanically honest share price, and the reciprocal rounds to zero downstream.
- Fresh-market inverse collapse — a newly listed or near-empty collateral wrapper makes it cheap to define the first meaningful price before the reciprocal transform runs.
- Configuration-induced inverse collapse — an operator or governance action wires in a mark whose scale or semantics are wrong, and inversion converts that mistake into near-zero or zero collateral cost.
- Sentinel-zero acceptance — the core arithmetic bug is not merely the reciprocal floor, but later code treating
0as benign state instead of impossible state.
Three reciprocal-price promotions reviewers should not collapse
-
A direct collateral mark exists at all
- A wrapper share price, oracle answer, exchange rate, or other upstream mark is available and may even be locally truthful.
- That is only the weakest claim:
some price-domain object exists.
-
That direct mark still counts as safely representable reciprocal truth
- The harder question is whether this specific mark remains inside the supported
K / markdomain, stays strictly nonzero after integer rounding, and preserves the semantics the borrow-side engine thinks the reciprocal has. - Most incidents in this family happen here: the protocol silently promoted
large but accepted direct markintomoney-good reciprocal stateeven though the reciprocal representation had already collapsed.
- The harder question is whether this specific mark remains inside the supported
-
That accepted reciprocal truth still deserves live borrow, liquidation, or collateral authority
- Even a reciprocal that is syntactically valid should not automatically inherit unlimited same-window borrowing or systemwide solvency authority without caps, quarantine, or other blast-radius controls.
- This is where collateral admission lag, isolated markets, and bounded-loss design still matter.
Healthy review gets materially better once teams stop flattening those three promotions into one vague claim like the collateral has a price.
- Boundary tests that keep this pattern distinct:
- Classify it here when the decisive bug is the reciprocal transform itself. If the preserved lesson is
the upstream mark could be locally correct or at least economically reachable, butK / markfloor-rounded to a solvency-critical zero, this note is doing the analytical heavy lifting. - Classify it under unit-normalization-failure when the main failure is that the protocol wired the wrong unit domain into the price path before reciprocal math ever became the interesting part. If the output was already dimensionally wrong as
cbETH / ETHversuscbETH / USD, or off by10^n, the reciprocal is downstream fallout rather than the core pattern. - Classify it under donation-share-price-manipulation when the main lesson is how the wrapper mark was printed and monetized, not how reciprocal borrow-side math collapsed. If the exploit would still read cleanly without talking about
inverse == 0, stay with the broader wrapper-price family. - Classify it under oracle-configuration failure when the protocol itself mainly published a bad mark and reciprocal floor-to-zero is only one consequence among several. There the operational lesson is
how did bad oracle truth reach production at all, nothow did a supported mark domain silently end at zero after inversion.
- Classify it here when the decisive bug is the reciprocal transform itself. If the preserved lesson is
- Detection heuristics:
- Search for reciprocal pricing in solvency code:
K / price,K / exchangeRate, or any normalization step where the protocol stores the inverse rather than the raw mark. - Trace the first publication site, not just the final consumer. Ask which function writes the reciprocal into cached market state, and whether later modifiers, liquidations, or helper checks silently trust that stored value.
- Fuzz collateral prices upward until the reciprocal reaches
0; if the harness does not revert before that point, the market likely has a catastrophic edge. - Re-run the same fuzzing in launch-state and dust-supply conditions, because reciprocal collapse is often cheapest when the upstream mark comes from a near-empty wrapper.
- Inspect comments and audits for real-number reasoning like
0.49...that the implementation silently floors to0. - Treat
0in any solvency-critical scalar as suspicious by default; most healthy systems should reject it long before user-facing borrow logic sees it.
- Search for reciprocal pricing in solvency code:
- Defenses:
- Reject reciprocal outputs of
0anywhere before they can influence collateral admission, borrow limits, liquidations, or health checks. - Prefer direct-price formulations when possible; every reciprocal transform creates a
large number becomes zeroboundary that needs explicit handling. - Cap or haircut donation-sensitive wrapper prices before they enter inverse-price math.
- Put the bound on both sides of the seam: oracle adapters should reject obviously abnormal wrapper marks before publication, and lending pairs should still treat
inverse == 0as impossible state even if the oracle is supposed to enforce the upper bound. - Keep reciprocal publication and reciprocal consumption as separate guarded steps. If a borrow or cache-refresh path writes the inverse into market state, that write itself should fail closed before any later solvency modifier or liquidation path can reuse it.
- Seed, offset, or quarantine fresh wrapper collateral so attackers cannot cheaply print the first economically meaningful mark.
- Add invariants and fuzz tests asserting that solvency-critical reciprocals stay within a nonzero safe range across realistic and adversarial price domains.
- Treat reciprocal math as part of the oracle boundary, not as harmless normalization.
- Reject reciprocal outputs of
What sound design looks like
- The protocol keeps collateral truth in the most intuitive domain possible. If solvency can be expressed directly in price or exchange-rate space, the design avoids storing a reciprocal just for convenience.
- The architecture keeps three states visibly separate:
a direct mark exists,that mark is still representable as safe reciprocal truth, andthat accepted reciprocal may now influence live borrow or liquidation authority. Healthy systems do not collapse those promotions into one reusableprice looks finebit. - When a reciprocal is unavoidable, the supported range is explicit and machine-enforced. Reviewers can point to the exact maximum upstream mark, the exact normalization constant
K, and the exact guard that preventsK / markfrom ever reaching0in production math. - Reciprocal publication is treated as a privileged state transition, not a harmless helper. If a borrow, sync, or cache-refresh path computes the inverse and stores it, that write path carries the same
nonzeroand range guards as the final solvency consumer. Zerois impossible state all the way down. Oracle adapters, cache writers, collateral admission paths, health checks, liquidation logic, and migrations all agree that a solvency-critical inverse of0meansstop, notfree collateral,unset, ordust.- Range ownership is duplicated across layers instead of delegated by folklore. The upstream oracle or adapter rejects abnormal direct marks, and the lending engine still rejects impossible reciprocals even if the upstream layer is later upgraded, bypassed, or misconfigured.
- Fresh and donation-sensitive collateral is treated as hostile to reciprocal math. A new vault, near-empty wrapper, or cheaply inflatable share price does not get to flow straight into reciprocal solvency logic until seeding, offsets, caps, or quarantine make the nonzero range economically robust.
- Monitoring and blast-radius controls assume one range check can fail. Caps, borrow-side quarantine, and fast pauses sit underneath the arithmetic so a single reciprocal-boundary miss cannot instantly turn one printed mark into systemwide bad debt.
- The clean design target is simple: a protocol should never be in a position where making collateral more expensive can make borrowing look safer.
Failure-prevention checklist
- Treat
direct mark exists,direct mark still maps to nonzero reciprocal truth, andthat reciprocal may now authorize live borrowing or liquidationas separate states in code, monitoring, and incident response. - Write one explicit supported domain sheet for every reciprocal price path: upstream economic object, units, min supported value, max supported value, normalization constant
K, and the first value at whichK / markfloors to0. - Enforce
inverse > 0as a hard invariant in every layer that can influence solvency: oracle adapters, cache writers, collateral admission, health checks, liquidation paths, and any migration or admin setter that can publish a reciprocal. - Prefer storing and validating the direct mark, then derive the reciprocal only at the narrowest possible consumption point; if the protocol stores the inverse, also store the accepted source-domain bound that proves the inverse cannot be zero.
- Put abnormal-price clamps upstream of publication and downstream of use. A wrapper, vault, or collateral adapter should reject absurd marks before they become canonical state, and the lending engine should still reject impossible reciprocals even if the upstream layer claims to sanitize them.
- Quarantine fresh, empty, or donation-sensitive collateral until attackers can no longer cheaply define the first economically meaningful mark through dead-share absence, virtual-offset absence, or raw-balance donation.
- Treat reciprocal math as a launch and migration concern, not just a steady-state math concern. New collateral listings, oracle swaps, decimal rewires, and adapter upgrades should all re-prove that the published mark stays inside the nonzero reciprocal range.
- Fuzz the full collateral path end to end with adversarial high prices, not just ordinary volatility bands. The important edge is often
truthful but attacker-reachable extreme, notobviously impossible nonsense. - Keep zero as impossible state, never as a sentinel for
free,safe,unset, ornot applicable, anywhere a reciprocal can flow into debt or LTV math. - Bound blast radius anyway with collateral caps, borrow-side quarantines, and fast pauses so one reciprocal-domain mistake cannot immediately socialize loss across the whole lending book.
- Document who owns the reciprocal boundary operationally: if the upper-bound check lives in the oracle today, downstream consumers should still know what invariant they are relying on and alert if it breaks.
Audit prompts
- Which reciprocal-price promotion is happening at each step here:
direct mark exists,direct mark still counts as nonzero reciprocal truth, orthat accepted reciprocal may now exercise live solvency authority? - Where exactly does the protocol invert a mark, exchange rate, or price, and what is the largest upstream value that still keeps the reciprocal strictly nonzero in deployed integer math?
- Does any solvency-critical path consume
0as syntactically valid reciprocal state because it was originally treated as default storage, missing data, or benign dust? - Can an attacker reach the reciprocal edge honestly through empty-vault donation, first-depositor control, wrapper inflation, stale-cache overwrite, or governance / admin configuration rather than through an obviously fake oracle answer?
- Which component is supposed to enforce the upper bound on the direct mark: the oracle, adapter, market, collateral manager, or liquidation engine? What happens if that component is bypassed, upgraded, or reused differently later?
- Are there any cached reciprocal values that can outlive the assumptions under which they were computed, such as after collateral migration, decimal change, wrapper replacement, or share-price jump?
- If the protocol uses reciprocal math for convenience, could it instead consume the direct price and keep all solvency logic in the more intuitive domain?
- Does the review harness fuzz upward price domains far enough to cross the
inverse == 0boundary, and does every reachable consumer revert before that state becomes borrow-relevant? - Are launch-state and dust-supply wrappers treated as special risk surfaces, or can a newly listed collateral path publish a first meaningful price directly into reciprocal solvency math?
- When a wrapper price is attacker-shaped but locally truthful, does the protocol have any second line of defense beyond trusting the oracle output to remain in range?
- If
inverse == 0were observed onchain today, which monitoring, pause, or cap mechanism would contain loss before the first large borrow completes?
Lessons that should already be standard
-
A collateral mark exists,that mark is still representable as nonzero reciprocal truth, andthat accepted reciprocal may now drive live solvency decisionsare three different claims. -
Reciprocal math is not harmless normalization. It creates its own failure boundary where
very large but still acceptedsilently becomesexactly zero, and that boundary deserves first-class review. -
Truthful upstream prices can still be unsafe downstream. If a manipulable wrapper can honestly report an absurd mark, the lending layer still owns the job of rejecting what its own arithmetic cannot safely represent.
-
Borrow functions, market syncs, and cache-refresh paths that publish a reciprocal into storage are acting as oracle writers in practice. They need the same impossible-state discipline as the final solvency check.
-
Zerois rarely neutral in solvency systems. In collateral math it usually meansimpossible, and treating it as merely another number is how expensive collateral turns into free borrowing. -
The supported domain of a price path is part of protocol design, not implementation detail. Teams should be able to say out loud what maximum mark their reciprocal math can safely absorb before shipping.
-
Fresh collateral is a math environment, not just a business rollout stage. If first-depositor economics can define the initial mark cheaply, reciprocal consumers are still in initialization mode no matter how production-ready the rest of the market looks.
-
Range checks belong on both sides of trust boundaries. Oracle-side clamps are good, but downstream consumers should still reject impossible reciprocals instead of assuming upstream publication will stay perfect forever.
-
Audits that verify formula correctness only in the direct-price domain can still miss the real exploit boundary. The attacker often monetizes the inversion seam, not the original mark itself.
-
A protocol that cannot explain who owns the
inverse must stay nonzeroinvariant across oracle, adapter, publication path, and lending layers is already relying on an undocumented security assumption. -
High-signal incidents:
- zero-exchange-rate exploit (2025-06-26) — a near-empty ERC-4626 collateral vault let a
2,000 crvUSDdonation print such a high share price that Resupply’s_updateExchangeRate()path computed1e36 / price = 0, and_isSolvent()then treated1 weiof collateral as supporting10,000,000 reUSDof borrow.
- zero-exchange-rate exploit (2025-06-26) — a near-empty ERC-4626 collateral vault let a
Internal linkages
- Best current exemplar: resupplyfi-cvcrvusd-donation-zero-exchange-rate-exploit-2025-06-26.
- Positive-design companion: Reciprocal price normalization and nonzero-invariant discipline — use this when the sharper question is what reciprocal guardrails, range checks, and impossible-state handling should have existed before inversion ever touched borrow-side math.
- Cross-family positive-design companion: Price-domain and reciprocal-range discipline — use this when the sharper follow-up is what one domain sheet should have made reviewers state both the final price semantics and the reciprocal-safe range together.
- Upstream price-printing family: donation-share-price-manipulation — use this when the main question is how the attacker inflated the wrapper mark in the first place.
- Launch-state companion: fresh-market initialization failure — use this when the sharper question is why the collateral path was allowed to go live while the attacker could still define its first meaningful economics.
- Adjacent arithmetic family: unit-normalization-failure — use this when the main problem is dimensional or decimal misinterpretation rather than a truthful or economically reachable large mark collapsing through reciprocal floor division.
- Broader publication failure family: oracle-configuration failure — use this when the sharper question is how the protocol published bad price truth at all, even if one downstream manifestation was reciprocal collapse.
- Downstream accounting family: solvency-check bypass — use this when the main question is not the reciprocal transform itself, but a broader helper path that lets bad debt bypass health checks.
Control surface
- The leverage sits where wrapper price, oracle output, or exchange-rate presentation crosses into reciprocal borrow-side math, then gets promoted again from
representable reciprocal stateinto live solvency authority. - These incidents are easy to misread as
just another donation attackorjust another bad oracle config, but the decisive failure is sharper: the protocol convertsextremely expensive collateralintozero-risk collateralby flooring the reciprocal to zero. - Resupply’s public post-incident code history is a useful implementation map: first reject obviously abnormal wrapper marks before inversion, then keep an explicit
inverse != 0guard even after the upper-bound check migrates into the oracle layer. - The practical invariant is simple: if a solvency-critical inverse can ever become
0, the system is one price spike away from treating impossible state as safety, and that boundary should be documented separately from ordinary decimal-normalization review. - The cheapest review habit is to require one end-to-end domain sheet that names the upstream economic object, final normalized output, reciprocal transform if any, and the exact maximum supported mark before the inverse becomes unsafe.
Sources
- https://rekt.news/resupplyfi-rekt
- https://ackee.xyz/blog/resupply-hack-analysis/
- https://www.quillaudits.com/blog/hack-analysis/resupply-hack-analysis
- https://raw.githubusercontent.com/SunWeb3Sec/DeFiHackLabs/main/src/test/2025-06/ResupplyFi_exp.sol
- https://github.com/resupplyfi/resupply/blob/main/src/protocol/pair/ResupplyPairCore.sol
- https://github.com/resupplyfi/resupply/blob/main/src/protocol/BasicVaultOracle.sol
- https://github.com/resupplyfi/resupply/commit/d84b3bf6319e974980b45b8b126ac93614836500
- https://github.com/resupplyfi/resupply/commit/c6a80f0720a6f0c9f3aea75ab8f983f62619a13c
Last reviewed
- 2026-07-31 UTC