Debt-Zero Emergency Exit Discipline

  • Name: Debt-zero emergency exit discipline
  • Category: lending reference / emergency-path discipline / helper-path invariant checklist / collateral-release safety
  • Summary: In debt-bearing systems, an emergencyWithdraw() or emergencyExit() path should default to one harsh rule: if debt is still live, user-directed collateral release is not an emergency exit; it is a solvency event. Healthy designs either require debt == 0 before collateral can leave through an emergency path, or force that path back through the same post-state solvency checks and unwind semantics the ordinary withdrawal path uses. The compact design rule is post-state meaning, not pre-state reassurance: an emergency helper may only release collateral after the system has proved the resulting debt state is safe, not merely after confirming the old pre-withdraw position still looked healthy. The sharper ladder stays explicit too: the protocol may skip optional operations because the user wants out urgently, the protocol may enter a narrower emergency-management mode for the position, and only later the protocol may actually release user-directed collateral after debt meaning is retired or safely re-proven. The dangerous mistake is to treat emergency logic as an operational shortcut that may ignore liability state because it was originally written before borrowing, leverage, or collateral reuse existed.
  • Why this note exists:
    • Platypus preserves the clean modern lesson: a helper path that looked harmless under staking only assumptions became a stablecoin-drain primitive once the same staked position also backed debt.
    • Many reviews ask does emergency withdraw still work? when the sharper question is what exact liability state must already be true before emergency collateral release is allowed at all?
    • The analytical win is to separate two very different things teams often collapse into one label: operational urgency versus economic finality. Urgency may justify skipping reward harvests, router choices, or gas-heavy convenience logic. It does not justify skipping the debt boundary.

Fast route inside this family

  • Platypus is the pure debt-bearing emergency-exit anchor: emergencyWithdraw() checked solvency before collateral actually left, so the path reused the name of the solvency boundary but not its post-state meaning. It is the clean post-state meaning, not pre-state reassurance exemplar inside this family.
  • Euler is the useful broader reserve-helper cousin: donateToReserves() was not branded as an emergency path, but it preserves the same deeper rule that collateral-affecting helpers must not outrun the post-mutation debt boundary.
  • Abracadabra Cauldron V4 is the aggregate debt-ledger cousin: it did not release collateral through an emergency label, but it preserves the same deeper warning that helper code can still author a weaker closeout boundary if debt state is allowed to drift out of canonical meaning.
  • Abracadabra GMX V2 is the pending-order phantom-collateral cousin: the helper-path problem moved to asynchronous collateral accounting rather than a classic emergency exit, but the same lesson survives — a secondary path defined a weaker economic finality boundary than the core solvency model.
  • The compact outward classifier for this seam is therefore: Platypus for stale pre-withdraw emergency exit, Euler for unchecked reserve donation, Abracadabra Cauldron V4 for aggregate debt-ledger corruption, and Abracadabra GMX V2 for pending-order phantom collateral.
  • Stay here when the sharper design question is specifically what must already be true before any debt-bearing position may use an emergency collateral-release path at all?
  • Step outward to helper-path-invariant-reuse-and-fail-closed-discipline when the issue is broader than emergency exits and the real lesson is that any helper, adapter, reserve, or bulk-management surface defined a weaker economic finality boundary than the canonical core.

Three emergency-exit promotions reviewers should not collapse

  1. Operational urgency exists

    • A user may need to bypass reward harvests, cooldown niceties, router choices, or other non-economic convenience logic.
    • That only answers should the system offer a faster operational lane?
  2. The protocol may enter a narrower emergency-management state

    • The system may freeze the position, disable optional side effects, or force a simpler unwind path while debt meaning is still being handled.
    • That is already stronger than the user wants out now, but it is still weaker than collateral may leave user-facing control immediately.
  3. User-directed collateral may actually be released

    • This is the strongest step: residual value leaves only after debt is zero or after the protocol has re-proved the post-state liability boundary through escrow, repayment, seizure, or equivalent closeout logic.
    • This note exists to keep that last promotion explicit. emergency mode exists was never automatically collateral may leave while debt still matters.

Healthy systems keep those steps separate. They do not let skip optional operations silently imply skip the debt boundary, and they do not let we entered panic mode silently imply the user may already leave with backing that liabilities still depend on.

The five joints a healthy debt-aware emergency exit should keep fused

  1. Explicit debt-zero precondition by default

    • If the path releases user collateral directly, the safe default is all debt obligations backed by that collateral family must already be zero.
    • Still solvent before withdrawal is weaker than no debt remains after withdrawal; Platypus failed exactly because those were treated as interchangeable.
    • A boolean returned from a helper like isSolvent() should never be allowed to stand in for the stronger statement this user may leave with the collateral and no liability cleanup is still owed.
  2. Post-state truth, not pre-state comfort

    • If a protocol intentionally supports emergency debt-bearing exits, it must evaluate solvency and release conditions against the post-withdraw state or an explicitly reserved unwind state.
    • Checks that run before collateral balances are reduced are only proving the old world was healthy.
    • This is the exact sequencing gap Platypus exposed: emergencyWithdraw() proved the position was healthy before collateral left, then transferred the collateral out anyway.
  3. One collateral-release core across ordinary and emergency paths

    • The safest shape is one internal reduce collateral / recompute debt safety / finalize release core, with emergency mode only skipping non-economic side work like reward collection, route choice, or optional bookkeeping.
    • Once the emergency branch open-codes its own weaker release logic, helper-path drift becomes likely.
    • The important design question is not whether the two functions share a name. It is whether they share the same economic finality boundary.
  4. Debt-aware meaning for every escape hatch label

    • Emergency, rescue, forceWithdraw, panic, unstakeNow, and similar labels often inherit social permission to bypass ordinary logic.
    • In a debt-bearing system those labels need explicit, narrow semantics: either debt must be zero, or this path escrows / liquidates / repays before any user collateral is released.
    • If the label’s meaning is just skip the main path, the protocol has probably turned an operational fallback into a liability bypass.
  5. Integration upgrades must re-audit old exits as new authority surfaces

    • A pre-existing staking, vault, or rewards contract can become part of the lending solvency boundary later, after debt modules are attached around it.
    • The dangerous question is not only is the new borrow module sound? but which old exits now release collateral that somebody else treats as backing?
    • Platypus is the clean reminder that legacy helper paths become new security-critical surfaces the moment collateral gets reused.

What sound design looks like

  • Default rule: no user collateral leaves while debt is live

    • The cleanest policy is simple: emergency withdrawal is only for debt-free positions.
    • If a user still owes debt, the protocol should route them into repay, liquidation, close-position, or explicitly debt-aware unwind logic instead of pretending collateral release is a harmless local action.
  • Emergency paths should skip operations, not liabilities

    • It can be reasonable to skip reward harvests, cooldown niceties, external swap routes, or optional accounting synchronization in emergencies.
    • It is not reasonable to skip the one state transition that proves liabilities remain safely covered.
    • The design habit worth preserving is that operational shortcut exists, position entered a narrower emergency state, and collateral may now be released are different promotions.
  • If debtful emergency unwind is truly required, make it a separate object

    • That path should look like freeze position -> compute post-unwind debt state -> repay / seize / escrow as needed -> release only residual user value.
    • In other words, debtful emergency unwind is not a faster withdraw; it is a controlled closeout flow.
  • Return values should encode the stronger economic question

    • Interfaces that expose (bool solvent, uint256 debtAmount) or similar multi-field answers should be consumed with debt semantics in mind.
    • If only the easiest boolean is enforced, the interface is already telling reviewers that economic meaning may be richer than the caller is honoring.
  • Legacy helper paths should fail closed after collateral reuse expands

    • When a staking receipt, vault share, or LP position becomes borrowable collateral, old emergency exits should usually revert until they are explicitly re-reviewed under the new liability model.
    • It used to be safe is the wrong baseline once collateral starts underwriting debt.

Useful positive design shapes

1. Zero-debt gate on collateral-release helpers

  • A direct require(debt == 0) or equivalent debt-family exhaustion check is often the right answer.
  • This is not over-conservative ceremony; it is making the real economic rule explicit.

2. Shared post-update solvency core

  • Ordinary and emergency release paths should converge on one internal routine that first mutates the collateral state, then proves the resulting debt state is still valid before value leaves.
  • Different UX wrappers can sit above that core; the solvency boundary should not.
  • The shorthand worth preserving is post-state meaning, not pre-state reassurance: the helper should only inherit the ordinary path’s authority if it also inherits the ordinary path’s post-mutation proof of safety.

3. Debtful emergency closeout as escrow, not withdrawal

  • If users must be able to react under stress while debt remains open, the safe design is usually temporary escrow or controlled liquidation of collateral rather than unrestricted collateral release.
  • That keeps panic mode from meaning borrower may leave the system with backing still attached to debt.

4. Integration-triggered helper requalification

  • Every time a previously local position becomes collateral for another module, old exits should be reclassified as solvency-critical surfaces and re-audited accordingly.
  • The review target is not just code correctness in isolation; it is whether the old helper now releases something another module prices as backing.

Design questions worth asking first

  • Does this emergency path ever release collateral that can also support debt elsewhere in the system?
  • Is debt == 0 enforced, or is the protocol relying on a weaker currently solvent check?
  • Does the safety check run against post-withdraw state, or only against the pre-withdraw position?
  • Do ordinary and emergency collateral-release paths share one economic finality core?
  • If debt remains live, why is this path releasing collateral to the user instead of routing through repay, liquidation, or escrow?
  • Which legacy helper functions became newly dangerous once the protocol let these assets back borrows, mints, or synthetic issuance?

Red flags that usually mean the emergency path is relying on folklore

  • This function was written before borrowing existed, but it's probably still fine
  • The user was solvent when we checked
  • Emergency withdraw only skips some accounting
  • The helper returns debt too, but we only need the boolean
  • Users are expected not to call emergency withdraw while borrowing
  • The normal withdraw path is debt-aware; this one is just for emergencies

Failure-prevention checklist

  • Put an explicit debt == 0 or debt-family exhaustion gate in front of every user-directed collateral-release helper unless the path is deliberately modeled as a debtful closeout flow.
  • Keep the three promotions mechanically separate in code and reviews: skip optional operations, enter emergency-management mode, and release user-directed collateral should not collapse into one generic emergency exit permission.
  • Route ordinary and emergency collateral release through one shared post-mutation validation core so the emergency wrapper can skip optional operations without inventing a weaker solvency boundary.
  • Evaluate emergency-exit safety against post-withdraw state or a tightly specified escrow / liquidation state, never only against pre-withdraw isSolvent() comfort.
  • Force debtful emergency behavior into a separate state machine: freeze the position, retire or escrow liabilities, then release only residual value instead of letting withdraw semantics masquerade as closeout semantics.
  • Requalify or disable legacy helper paths the moment a staking receipt, LP position, vault share, or wrapper becomes collateral for debt elsewhere in the system.
  • Treat helper return values as economic objects, not convenient booleans; if an interface surfaces debt amount, liability class, or unwind status, callers should enforce the strongest relevant field rather than the easiest one.
  • Add adversarial tests for borrow -> emergency withdraw, mixed-collateral debt families, partial-debt remnants, stale pre-state solvency reads, and helper reuse after collateral-role expansion.
  • Keep emergency powers narrower than normal withdraw powers: panic mode should remove options and slow value release, not create a faster path around debt settlement.

Audit prompts

  • Does any emergency, rescue, panic, forceWithdraw, or unstakeNow path release collateral that also backs live borrowing, minting, or synthetic liabilities?
  • Where is debt == 0 actually enforced, and if it is not, what exact post-state proof substitutes for it before collateral leaves user control?
  • Does the helper prove safety after collateral is reduced, or does it only prove the old position was healthy before the release?
  • Are ordinary and emergency exits truly sharing one economic-finality core, or do they only share names, helper calls, or surface-level variables?
  • If the protocol claims to support debtful emergency exit, where are repay, liquidation, escrow, haircut, or reserve-consumption semantics specified and tested as a separate closeout object?
  • Which helper paths predate borrowing or collateral reuse, and what explicit requalification happened before they were left callable in the richer system?
  • Do interface return values expose stronger debt semantics than the caller is enforcing, such as ignoring debt amount or liability class while trusting a single boolean?
  • If an attacker deliberately calls the emergency path at maximum leverage, what exact invariant prevents collateral from leaving while debt remains socially or economically live?

Lessons that should already be standard

  • Emergency is an operational label, not permission to bypass the liability boundary.
  • The user needs a fast lane, the protocol entered a narrower panic-state machine, and collateral may actually leave while debt matters are three different security claims.
  • Solvent before withdrawal is not the security claim; safe after collateral leaves is.
  • Debt-bearing emergency unwind is a closeout problem, not a faster withdraw problem.
  • Old helper code becomes new solvency code the moment its released object starts backing debt elsewhere.
  • A helper that exposes richer debt semantics than its callers enforce is already advertising that the implementation and the economic model have drifted apart.
  • If users are merely expected not to use an emergency path while borrowing, the protocol has left a liability rule in folklore instead of code.
  • Panic-mode UX should narrow authority and preserve accounting truth, not widen the user’s ability to leave with backing still attached to obligations.

How to use this note with the exploit corpus

  • Start here when the sharper design question is what should an emergency collateral-release path require before any debt-bearing position may exit?
  • Drop into accounting desynchronization when the main task is classifying the broader family of helper paths that let liabilities and backing drift apart.
  • Pair with borrower-separation discipline when the next question is how attacker-controlled liquidation or same-family capital can monetize the bad state once it exists.
  • Step outward to helper-path-invariant-reuse-and-fail-closed-discipline when Platypus is no longer specific enough and the sharper routing question becomes which broader helper-path cousin failed: stale pre-withdraw emergency exit, unchecked reserve donation, aggregate debt-ledger corruption, or pending-order phantom collateral.
  • Pair with Exit right consumed-state binding when the danger is not live debt but whether emergency and ordinary exits are consuming the same entitlement object at all.
  • Pair with Vault share truth as a single security object when the released object is a vault share or receipt token whose meaning has already spread into collateral admission.

Internal linkages

Control surface