Unsupported Standard-Verb Revocation and Fail-Closed Surface

  • Name: unsupported standard-verb revocation and fail-closed surface
  • Category: standards-integration reference / inherited-surface retirement note / interface-hardening checklist / deauthorization companion
  • Summary: Some protocols customize a standard so heavily that only one or two familiar verbs remain safely meaningful. At that point the design question is no longer just how do sibling verbs stay equivalent? but how do unsupported inherited verbs become truly dead instead of socially discouraged? Healthy systems make that non-support explicit onchain: stale inherited verbs revert, preview and quoting helpers do not advertise functionality that the product will not honor, wrappers and routers cannot silently rediscover the dead path, and upgrades keep the unsupported selector family deauthorized. This note exists as the narrower positive-design companion to shared-standard entrypoint invariant drift and entrypoint equivalence under shared standards when the right answer is not preserve equivalence across all sibling verbs but keep only one safe surface and prove the others are really gone.

Why this note exists

  • Many standard-integration failures start with a team believing an inherited path is not part of the product anymore even though the EVM still treats it as live public authority.
  • Entry-point equivalence is the best answer when multiple sibling verbs should remain safe economic synonyms.
  • But once a protocol intentionally keeps only one safe route, the sharper task becomes explicit deauthorization of the unsupported sibling family.
  • That makes this note a contract-local cousin of deprecation without deauthorization: one inherited verb can be deprecated socially while still retaining live mint, redemption, or solvency authority technically.

What this note is trying to preserve

  • If a verb is unsupported, the chain should say so decisively.
  • The public interface should not keep advertising semantics the contract no longer safely provides.
  • A stale selector should not become live again because of inheritance, upgrade drift, wrapper routing, or residual helper reuse.
  • Unsupported should mean cannot create or retire value, not merely not mentioned in the docs.

The six questions a fail-closed standard surface should answer

1. Which exact selectors are intentionally dead?

  • Write down the full family, not just the most obvious public function.
  • For an ERC-4626-style customization, that may include mint(), deposit(), withdraw(), redeem(), preview helpers, max helpers, wrapper shortcuts, native-asset helpers, and router entrypoints that can still reach the same primitive indirectly.
  • If reviewers cannot enumerate the dead selector set, they cannot prove it stays dead.

2. What is the onchain failure mode?

  • Unsupported verbs should revert explicitly and early.
  • A safe design should not rely on downstream arithmetic, empty balances, missing approvals, or accidental underflow to make the path unusable.
  • The revert reason does not need to be elegant, but the failure mode should be intentional and stable enough for tests and integrators to recognize.

3. Do preview and discovery helpers still lie?

  • Preview-style helpers, quote methods, max* functions, routers, ABI surfaces, and interface introspection can keep re-advertising a dead verb even if the state-changing call now reverts.
  • If a verb is unsupported, the surrounding discovery surface should stop implying that it works normally.
  • A product can choose between strict approaches:
    • revert on unsupported preview helpers too
    • return zero / no-capacity style outputs where the standard permits it
    • move to a narrower custom interface instead of preserving misleading standard breadth

4. Can wrappers or sibling helpers still rediscover the dead path?

  • It is not enough for the top-level selector to revert if another helper, router, inherited wrapper, or internal dispatch branch still reaches the old logic.
  • Deauthorization should be checked at the economic boundary, not only at the first external selector.

5. How will upgrades prove the dead surface stayed dead?

  • Unsupported inherited verbs often reappear when a later upgrade reimports a base contract, changes override ordering, or reopens an internal primitive without revisiting the whole selector family.
  • The dead surface needs regression tests and explicit review attention during upgrades.

6. What do integrators observe?

  • Routers, vault adapters, SDKs, and indexers often treat standard-shaped ABI presence as evidence of support.
  • If the protocol keeps the broader interface for compatibility reasons, it should still publish machine-readable or plainly testable signals that the unsupported route is intentionally unavailable.

What sound design looks like

  • Explicit revert on unsupported sibling verbs.
    • The contract rejects the unsupported path before any claim quantity, inventory movement, or liability retirement can become ambiguous.
  • No soft dependence on ambient state for safety.
    • This usually fails because the pool is empty is not deauthorization.
  • Preview and max helpers do not advertise false support.
    • If an integrator asks whether the route is usable, the answer should not still look like yes until the final state-changing call explodes.
  • Internal helpers are dead at the same economic layer.
    • A thin router or alternate wrapper should not be able to revive the retired path by reaching the same stale primitive.
  • Upgrade reviews treat dead selectors as protected invariants.
    • Unsupported today should still be unsupported after inheritance reshuffles, proxy upgrades, and new helper additions.
  • Documentation matches code, but code does the real work.
    • Docs may warn users away; only the revert makes that warning security-relevant.

The minimum test discipline this note wants

1. Dead-selector assertions

  • For every intentionally unsupported verb, assert an explicit revert under ordinary funded conditions, not only under empty or accidental failure states.
  • Test both direct calls and any router or helper path that could still dispatch into the same logic.

2. Preview-surface assertions

  • If preview, quote, or max* helpers remain exposed, assert that they either revert, return a deliberately non-usable output, or otherwise align with the disabled route.
  • The discovery surface should not say call me while the execution surface says surprise, unsupported.

3. Upgrade regression tests

  • Carry a snapshot list of unsupported selectors across upgrades.
  • Assert that new base classes, helper additions, and override-order changes do not silently reactivate them.

4. Same-boundary fallback tests

  • Force wrapper, native-asset, buffer, and residual routing states so that no alternate entrypoint can reach the stale economic primitive after the official selector is disabled.

5. Interface-compatibility assertions

  • If the protocol intentionally preserves a broad standard interface for external compatibility, test the compatibility story explicitly:
    • what functions remain callable
    • what functions intentionally revert
    • what helpers signal non-support
    • what adapters are expected to use instead

Common red flags

  • Users are supposed to use depositETH(), not mint().
  • We disabled the UI route, so the inherited function is effectively gone.
  • That selector only fails because the pool is empty right now.
  • The state-changing verb reverts, but previewMint still returns a normal answer.
  • A router cannot call it directly, but an internal helper still reaches the same stale primitive.
  • We preserved the standard ABI for compatibility, but never wrote down what unsupported means for integrators.
  • The upgrade was small, so we did not rerun dead-selector tests.

Failure-prevention checklist

  • Enumerate every intentionally unsupported selector and helper in one maintained manifest: state-changing verbs, preview helpers, max* helpers, wrappers, routers, native-asset aliases, and any internal dispatch path that can still reach the same economic primitive.
  • Make unsupported verbs fail closed at the economic boundary, not merely at a convenience wrapper. The path should revert before value creation, value retirement, liability release, or reserve recognition becomes ambiguous.
  • Do not rely on ambient emptiness, paused liquidity, missing approvals, or accidental underflow as the disabling mechanism. If the route is dead, the contract should say so even under otherwise healthy funded conditions.
  • Keep discovery surfaces aligned with execution reality. Preview helpers, quoting helpers, SDK metadata, router registries, and interface introspection should not continue to advertise normal support for a selector family you no longer honor.
  • Test wrapper and sibling-route non-reachability explicitly. If mint() is dead but depositETH(), zapDeposit(), or an inherited internal helper can still land on the same stale mint primitive, the path is not actually dead.
  • Treat broad ABI compatibility as a security commitment, not just an integrator convenience. If you keep a standard surface for ecosystem reasons, publish the exact supported subset and machine-test it continuously.
  • Carry unsupported-selector invariants through upgrades, migrations, inheritance reshuffles, proxy implementation swaps, and router additions. Dead today must still be dead after refactors.
  • Bind deauthorization to economic semantics, not names alone. If a renamed helper, alternate calldata envelope, or wrapper entrypoint can still exercise the retired authority, the authority never actually left.
  • Require incident and launch reviews to answer what unsupported route could become live again here? whenever a protocol customizes a standard down to one safe path.
  • Prefer narrower explicit interfaces over misleading broad ones when compatibility no longer outweighs the risk of stale selector ambiguity.

Audit prompts

  • Which exact selectors, helper functions, and indirect routes are intentionally unsupported here, and where is that list written down so reviewers can regression-test it?
  • Does the unsupported path revert because the protocol intentionally deauthorized it, or because some unrelated state just happens to make it fail today?
  • If a standard ABI remains exposed, what do preview*, max*, quote helpers, routers, and SDK discovery surfaces currently signal to integrators about that dead path?
  • Can any wrapper, native-asset helper, fallback branch, inherited internal function, or router adapter still reach the same stale economic primitive after the official selector was disabled?
  • During an upgrade, what concrete tests prove that override ordering, inherited base logic, newly added helpers, or proxy refactors did not silently reactivate the retired surface?
  • What exact economic authority did the unsupported verb used to carry: minting, redemption, collateral release, liability retirement, reserve movement, or balance-sheet publication?
  • If the protocol claims only deposit is supported now, what proves mint, withdraw, redeem, or adjacent verbs cannot still create a different backing story from the same reserve object?
  • Are integrators expected to infer non-support socially from docs or UI hints, or can they observe it mechanically from code, tests, and machine-readable signals?
  • If the unsupported selector reappeared for one block after an upgrade or wrapper change, what value could it create or retire before anyone noticed?
  • Would a router author, aggregator, or auditor looking only at ABI presence and helper outputs conclude the dead route is safe to call? If yes, the fail-closed story is incomplete.

Lessons that should already be standard

  • We do not support that path is not a product statement until the contract enforces it.
  • Standard inheritance is live authority inheritance. If a selector remains reachable, it remains part of the protocol’s security boundary whether or not the docs still mention it.
  • UI removal is not deauthorization. Attackers integrate against bytecode, routers, and helper surfaces, not your happy-path frontend.
  • Preview and discovery functions are part of the control surface. A stale previewMint() can be security-relevant even if mint() now reverts, because it keeps external systems routing into a dead or dangerous branch.
  • Broad compatibility is only virtuous when the broader surface still tells one true economic story. Otherwise the honest answer is a narrower interface and an explicit revert.
  • Dead selectors deserve the same regression discipline as live ones. Refactors, upgrades, inheritance reshuffles, and new wrappers routinely resurrect paths teams thought were gone.
  • Unsupported should mean cannot move money, create claims, or retire liabilities, not merely users are discouraged from clicking it.
  • If a protocol keeps one safe standard verb and deauthorizes its siblings, the deauthorization itself becomes a first-class solvency invariant.

Where this note helps most

Internal linkages

Control surface