Price-Domain and Reciprocal-Range Discipline

  • Name: price-domain and reciprocal-range discipline
  • Category: oracle-and-solvency reference / dimensional-review checklist / reciprocal-boundary checklist
  • Summary: Many lending incidents do not fail because the protocol had no price. They fail because the protocol never wrote down what price domain it expected, what reciprocal range it could safely support, or what states were impossible at the seam between oracle publication and borrow-side math. Healthy systems treat asset / quote identity, decimal scale, reciprocal transform, and zero-floor behavior as one review object. This note exists to preserve the design rule that every solvency-critical price path should publish both the supported output units and the supported reciprocal range before the path is allowed to control borrowing, liquidation, or minting.
  • Why this note exists:
    • Oracle incidents are often split too early into wrong units versus inverse rounded to zero, even though both failures begin with one missing artifact: the protocol never made its supported price domain explicit.
    • Teams regularly verify freshness, liveness, and source identity without forcing themselves to say what exact units does this path output? and what exact reciprocal range can downstream code safely consume?
    • The useful design habit is to review one end-to-end price object: upstream quantity, final normalized output, downstream reciprocal transform if any, and explicit impossible-state handling.

What this note means by price-domain and reciprocal-range discipline

  • Price-domain discipline means the protocol can state the exact final solvency object in plain terms: this path returns <asset>/<quote> in <scale> after <specific transforms>.
  • Reciprocal-range discipline means the protocol can also state the downstream borrow-side domain: if we invert that output as K / price, the supported upstream range is <X>, the supported reciprocal range is <Y>, and zero is impossible state.
  • The design goal is not just normalize correctly. It is make the supported semantic domain and the supported arithmetic domain explicit before balance-sheet logic trusts either one.

Three price-object promotions reviewers should not collapse

1. A source path can print some price-looking number

  • A feed, wrapper adapter, composite oracle, or migration branch can return a nonzero integer that is locally well-formed.
  • This only proves some number exists. It does not yet prove the protocol has named the right economic object, the right quote asset, the right decimal scale, or a reciprocal domain the borrow side can safely consume.

2. The protocol can publish that number as final local solvency truth

  • The next promotion is stronger: the system has decided that the branch output is already the canonical asset / quote object it wants downstream code to trust.
  • This is where missing quote legs, decimal mismatches, wrapper-ratio confusion, and silent reciprocal-domain assumptions stop being integration trivia and become balance-sheet policy.
  • A price path is already in danger when some branch returned a number silently becomes this is the final money-good mark for this market.

3. Downstream code may spend against that published truth

  • The last promotion is that borrowing, liquidation, minting, redemption, reserve accounting, or health-factor logic is allowed to act on the published mark or its reciprocal transform.
  • This is where many incidents actually pay: the protocol did not merely publish an unstable or mis-specified price object, it let a live balance-sheet path spend against it before any guard, quarantine, or de-recognition step intervened.
  • Healthy reviews keep number exists, number is the final solvency object, and downstream code may already spend against it visibly separate.

The main design rules

1. Write the final solvency object as dimensional algebra

  • Reviewers should be able to say, on paper, what the final output means.
  • A healthy oracle review artifact looks like:
    • raw source: cbETH / ETH at 1e18
    • quote leg: ETH / USD at 1e8
    • final output: cbETH / USD at 1e18
    • downstream consumer: collateralValue = amount * price / 1e18
  • If the team cannot write that sentence clearly, the price path is not ready for production.

2. Publish both the supported output units and the supported downstream transform

  • Some paths end at the final market price. Others hand that price into K / price, 1 / exchangeRate, or another reciprocal normalization step.
  • The review object should therefore include both:
    • semantic domain — what the number means
    • arithmetic domain — what numeric range downstream code can safely consume
  • This keeps teams from proving only the feed is dimensionally right while never asking whether the borrow engine can safely invert the result.

3. Treat dimensional correctness and reciprocal safety as one seam

  • A price path can fail before inversion because the output is in the wrong unit domain.
  • It can also fail after a locally correct output because reciprocal math floors the inverse to zero.
  • Healthy design review checks both in one pass:
    • are the units right?
    • is the scale right?
    • does the downstream transform preserve a nonzero safe range?
    • what exact outputs are impossible and should revert?

4. Make impossible states explicit

  • price == 0 is usually impossible state.
  • inverse == 0 is also usually impossible state.
  • asset / quote outputs that differ by several orders of magnitude from the supported market domain may also be impossible state even if they are syntactically valid integers.
  • A healthy system does not leave those decisions implicit inside comments or operator folklore. It encodes them as guards, pauses, quarantines, or bounded fallbacks.

5. Review launch and migration paths against the same domain sheet

  • The useful artifact is not only for new protocol design. It matters at:
    • market launch
    • oracle migration
    • wrapper migration
    • new collateral admission
    • fallback-branch activation
    • defensive-overlay rollout
  • If a protocol needs a different unit interpretation or reciprocal bound after a migration, that is a balance-sheet change, not a mere implementation detail.

6. Fuzz for boundary crossing, not only for ordinary prices

  • Good tests should push the output across both semantic and arithmetic edges:
    • wrong quote leg
    • wrong decimal exponent
    • enormous but still representable wrapper prices
    • reciprocal floor to zero
  • The useful question is not only does the number look right in normal conditions? but what exact guard fires first when the path leaves its supported domain?

What sound design looks like

  • The protocol can say which promotion boundary it is crossing.
    • Reviewers and operators can distinguish a branch emitted a number, the system accepted that number as the final price object, and borrow / liquidation / mint logic may now spend against it instead of collapsing those steps into one ambient price updated event.
  • The protocol can name the final price object plainly.
    • Example: this path returns cbETH/USD scaled by 1e18 is better than this path returns a normalized price.
  • The protocol can name the supported reciprocal domain plainly.
    • Example: the borrow engine inverts this as 1e36 / price and rejects any mark above X because that would floor the reciprocal below 1.
  • Every branch is reviewable separately.
    • Primary path, fallback path, wrapper path, and emergency path each preserve the same semantic domain and supported range, or they fail closed.
  • Impossible states are encoded, not narrated.
    • If the protocol believes inverse == 0 or cbETH ~= $1 is impossible under the supported configuration, it should reject that state directly.
  • Tests cross both seams.
    • One family checks dimensional correctness; another checks reciprocal nonzero safety.

Useful positive design shapes

1. Domain sheets in market-listing and migration reviews

  • Every new market or migration should ship with one short domain sheet:
    • raw sources and their units
    • final normalized output and its scale
    • downstream consumer formula
    • reciprocal transform, if any
    • supported min/max range
    • explicit impossible states
  • This is the cheapest habit that prevents a large share of misconfiguration incidents from hiding as implementation trivia.

2. Dual-side guards

  • The oracle side should reject semantically invalid outputs.
  • The lending side should reject arithmetic states it cannot safely consume.
  • Healthy systems overlap the defenses instead of assuming one side’s correctness makes the other side’s checks unnecessary.

3. Branch-local invariants

  • Mixed-source or fallback-heavy oracles should state the domain for each branch independently.
  • The right question is not only does getPrice() return an integer? but does every branch return the same economic object in the same scale, and does every downstream transform still stay inside its safe range?

4. Collateral quarantine for fresh or donation-sensitive wrappers

  • When a wrapper can print extreme prices cheaply, reciprocal-range review and collateral-admission review should travel together.
  • If the wrapper’s supported domain is still mostly theoretical, the protocol should narrow borrow-side authority long before reciprocal collapse becomes reachable.

Negative exemplar cluster: what no explicit domain sheet looks like

  • bad-debt liquidations (2026-02-15) — the protocol published cbETH / ETH as if it were already cbETH / USD, so the semantic domain was wrong before any later lending math ran.
  • whitehat (2023-03-07) and USDC market oracle decimal misconfiguration (2024-10-13) — the protocol published the wrong scale, so the arithmetic domain was already broken at the final price output.
  • zero-exchange-rate exploit (2025-06-26) — the upstream wrapper price was economically reachable, but the borrow engine had no explicit reciprocal-range discipline and accepted 1e36 / price = 0 as if it were valid solvency state.
  • The preserved common lesson is not merely one path had bad units and another path had bad reciprocal math. It is that the protocols never forced themselves to publish one supported end-to-end domain sheet before those paths controlled real money.

Red flags that usually mean the seam is underdesigned

  • The feed address is correct, so the output must be correct
  • The price looks normalized enough
  • The reciprocal is just a harmless internal transform
  • Zero can only happen if the price gets absurdly high without any enforced bound proving that claim
  • The primary path is fine when no one can state the branch-local units and range for fallbacks or wrapper branches
  • comments that reason in reals while the implementation floors integers to zero

Design questions worth asking first

  • What exact economic object does this path publish: asset / quote, wrapper ratio, LP NAV, or something else?
  • What exact scale does it publish in?
  • Which branch, adapter, or migration step is responsible for the final normalization into the solvency domain?
  • Does downstream code consume the raw mark directly or invert it first?
  • If it inverts the mark, what is the largest supported upstream value before the reciprocal becomes unsafe or zero?
  • Which states are explicitly impossible and how does the system fail when they appear?
  • Can operators, auditors, and governance reviewers recover all of that from one short artifact without reconstructing it from several contracts and comments?

Failure-prevention checklist

  • Require one domain sheet for every solvency-critical price path that names the raw inputs, branch-local transforms, final output units, scale, downstream consumer formula, reciprocal transform if any, and explicit impossible states.
  • Make the three promotions explicit in review artifacts and monitoring: branch emitted a number, protocol accepted that number as final solvency truth, and downstream code may now spend against it should be separately visible rather than implied.
  • Keep semantic-domain review and arithmetic-range review in the same approval step so a path cannot launch just because the feed address is correct or the decimals look familiar.
  • For every branch — primary, fallback, migration, emergency, or wrapper-specific — prove it returns the same economic object in the same scale, or fail closed instead of silently widening the accepted domain.
  • Write down the maximum supported direct mark and minimum supported reciprocal before launch; if nobody can state them, the protocol is still depending on folklore rather than an owned invariant.
  • Reject impossible states on both sides of the seam: semantically invalid final prices upstream and arithmetically invalid downstream states like inverse == 0 or out-of-domain scale jumps.
  • Re-run the same domain-sheet review on every oracle migration, collateral migration, wrapper replacement, decimal rewiring, and new listing. Those are balance-sheet changes, not bookkeeping chores.
  • Fuzz and invariant-test both classes of boundary crossing: wrong units / wrong scale and truthful-but-extreme direct marks that break reciprocal safety.
  • Keep collateral caps, borrow-side quarantine, and fast pause controls under the price path so one domain-sheet miss cannot immediately socialize loss across the whole market.

Audit prompts

  • What exact economic object should this path publish at the final consumer boundary, and can reviewers write it down as dimensional algebra without guessing?
  • Which branch actually performs the last normalization step into solvency truth, and do all sibling branches preserve that same semantic object?
  • What exact scale and quote asset does each branch output, and where would a wrong-unit or wrong-decimal branch first be caught?
  • Does downstream code use the direct mark or a reciprocal, and what exact bounds make that arithmetic safe in deployed integer math?
  • Are impossible states explicitly encoded, or are they only implied by comments such as this should never happen?
  • Which launch, migration, or fallback paths can bypass the reviewed domain sheet in practice?
  • Can a fresh wrapper, thin market, or attacker-shaped local mark push the path out of its supported arithmetic domain even if the semantic units remain locally correct?
  • If the published price left its supported domain today, which guard would fire first: upstream publication rejection, downstream revert, collateral de-recognition, market pause, or nothing at all?

Lessons that should already be standard

  • A correct feed address is not the same thing as a correct solvency object.
  • A branch returned a number, the protocol accepted that number as final price truth, and downstream code may spend against it are three different claims. Good reviews keep those promotions separate.
  • Unit correctness and reciprocal safety are not separate nice-to-haves; they are one joined balance-sheet boundary.
  • Domain sheets are security artifacts, not documentation garnish. If a market cannot explain its final price object in one short page, it is not ready to control borrowing.
  • Fallback branches and migrations are where semantic drift hides. Primary path is correct does not help if a secondary path publishes a different economic object under the same interface.
  • This value should never reach zero is not a control until the code proves what stops it.
  • Fresh or manipulable wrappers can keep semantic units locally correct while still pushing arithmetic outside the supported reciprocal range.
  • Reviewers need both plain-language meaning and machine-range bounds. A number can be dimensionally correct and still economically unusable downstream.
  • When semantic domain, scale, and reciprocal-safe range are reviewed separately by different people, nobody really owns the solvency object.

How this note complements the exploit corpus

Internal linkages

Control surface