Helper-Path Invariant Reuse and Fail-Closed Discipline
-
Name: helper-path invariant reuse and fail-closed discipline
-
Category: protocol-design reference / helper-path discipline / canonical-state-transition checklist / positive-design companion to lending-accounting bypasses
-
Summary: When a protocol already has one canonical path that mutates economically meaningful state safely, helper paths should almost never open-code a weaker cousin.
emergencyWithdraw(),repayForAll(), reserve-donation helpers, migration shortcuts, rescue functions, optimistic management multicalls, asynchronous-order collateral adapters, and bulk-accounting utilities should either route through the same invariant-preserving core or fail closed until they are requalified under the new architecture. The recurring failure is not justa helper had a bug; it isa secondary path quietly became an alternative economic finality boundaryby collapsing three promotions that deserve to stay separate:a side path is reachable at all,that side path is allowed to author one canonical post-action state story, andlater borrow / liquidation / refund / migration / reserve-spend paths are still allowed to cash out against that helper-authored story. Euler, Platypus, Arcadia Finance, Team Finance, Abracadabra Cauldron V4, and Abracadabra GMX V2 preserve six useful shapes of that lesson: unchecked reserve donation, stale pre-withdraw emergency exit, optimistic collateral release with sibling liquidation reentry, migration-closeout identity mismatch with refund capture, aggregate debt-ledger corruption, and pending-order phantom collateral that liquidation can consume without retiring the same solvency value. -
Why this note exists:
- Several exploit families are easier to recognize once the reviewer asks
which path is the real economic truth surface? - Teams often re-audit the main borrow, withdraw, liquidation, or migration flow after a feature expansion, but leave old helpers, emergency exits, or bulk-management functions carrying assumptions from a simpler era.
- Reviewers also flatten
this helper is still callable,this helper is still allowed to publish an economically meaningful result, andthe rest of the protocol may still spend against that resultinto one vague claim that the helper ispart of the same feature. - The useful design question is often not
does the helper still run?butwhy is this helper allowed to define its own weaker version of the same accounting boundary at all?
- Several exploit families are easier to recognize once the reviewer asks
Fast route inside this family
- Euler is the unchecked reserve-donation / self-liquidation anchor: a reserve helper deleted collateral without reusing the same post-mutation liquidity boundary as other eToken balance changes.
- Platypus is the stale pre-withdraw emergency-exit anchor:
emergencyWithdraw()checked solvency before collateral actually left, so the helper reused the name of the solvency boundary but not its real sequencing. - Abracadabra Cauldron V4 is the aggregate debt-ledger corruption anchor:
repayForAll()leftelasticandbasein a state the main borrow path would never have authored. - Arcadia Finance is the optimistic-management / sibling-liquidation anchor:
vaultManagementAction()deliberately moved collateral out before the final health check, andliquidateVault()was still allowed to rewrite debt state mid-flight. - Team Finance is the migration-closeout identity-mismatch anchor:
migrate()authenticated one attacker-owned lock record while burning and refunding a different victim LP position, so helper-local migration and residual-routing logic quietly became the real custody boundary. - Abracadabra GMX V2 is the pending-order phantom-collateral anchor: a liquidation-side helper extracted real RouterOrder value without consuming the same order state later trusted by
_isSolvent(). - Stay here when the sharper design question is not only
which accounting invariant broke?butwhy was a helper, adapter, emergency path, liquidation side path, or bulk-management surface allowed to define a weaker economic finality boundary than the canonical core?
What this note means by canonical-state-transition reuse
- A canonical state transition is the internal routine or tightly equivalent sequence that mutates economically important state and then proves the resulting state is still valid.
- Helper-path invariant reuse means secondary wrappers reuse that same core instead of reimplementing their own looser mutation order, guard set, or accounting interpretation.
- Fail closed discipline means that once protocol architecture changes make an old helper’s assumptions uncertain, the helper should revert, pause, or narrow its authority until it is explicitly requalified.
Three helper-authority promotions healthy reviewers should not collapse
- A side path is reachable at all
- A helper, rescue path, emergency exit, migration wrapper, reserve-management utility, or asynchronous adapter callback can still touch a live balance-sheet object.
- That only answers
can this path still run and mutate something economically relevant?
- That side path is still allowed to publish one canonical post-action state story
- The protocol treats the helper’s local sequencing, returned object, or intermediate bookkeeping as good enough to stand in for the canonical mutation-and-validate core.
- This is already a stronger claim than reachability. It answers
does the system accept this helper's version of completion, collateral release, debt retirement, refund identity, or reserve movement as if it were the canonical one?
- Later paths are still allowed to spend against that helper-authored story
- Borrow, liquidation, migration refund, reserve spend, redemption, or cross-module accounting paths then keep honoring the state the helper authored.
- This is the still-stronger promotion:
is the rest of the protocol allowed to cash out against the helper's weaker state story as if the real economic boundary had already been crossed?
Most expensive helper-path failures collapse those three promotions into one vague comfort claim that this is only another way to do the same thing. The more useful review split is why was the helper callable, why did its local state story count as canonical, and why were later cash-out paths still allowed to trust it?
The main rules healthy helper paths should obey
1. One economic boundary, many UX wrappers
- Different entrypoints may exist for UX, gas, batching, panic handling, or admin convenience.
- But once they change collateral, debt, reserves, claims, or other balance-sheet objects, they should converge on one invariant-preserving core.
- The dangerous shape is
same economic effect, different safety boundary.
2. Helpers should skip optional work, not the truth-producing step
- It can be fine for emergency or bulk paths to skip reward harvests, event niceties, route search, or convenience bookkeeping.
- It is not fine for them to skip the state mutation ordering or post-state validation that makes the economic action safe.
- Platypus is the clean reminder: the emergency wrapper skipped the meaningful solvency boundary by checking before collateral had actually left.
- Arcadia is the sibling-path reminder: an optimistic management helper that intentionally delays validation is only safe if every other path that can rewrite debt, collateral status, or liquidation state is locked out until that validation finishes.
3. Pre-feature helpers should fail closed after collateral or debt reuse expands
- A helper written for
staking only,repayment only, orlocal accounting onlycan become dangerous once the same object later backs debt, redemption rights, or cross-module solvency. - After that expansion, the old helper should default to revert, narrow authority, or reuse the now-canonical debt-aware path until explicitly re-reviewed.
It used to be harmlessis not a security argument once the surrounding system has changed what the object means.
4. Bulk-management helpers still own real invariants
- Functions with names like
repayForAll,sync,donateToReserves,migrate,sweep,rescue,manage, or adapter-sidesendValue/closeOrderstyle utilities often sound administrative or mechanical. - If they mutate aggregate debt, collateral balances, reserve balances, liquidation eligibility, or entitlement objects, they are not mere convenience code; they are economic-control surfaces.
- Abracadabra is the clean reminder that a debt-management helper can poison the same aggregate state the main borrow path later prices as truth.
- Arcadia is the complementary reminder that even a user-facing
managehelper becomes an economic-control surface once it can temporarily externalize collateral before the shared solvency boundary reasserts itself.
5. Callers should not be asked to remember social rules the code can encode
Users should not emergency withdraw while borrowingandnobody should call this helper in that stateare folklore, not defenses.- If the helper is only safe in a narrow state, encode that state explicitly with reverts or tightly scoped authority.
- Good fail-closed design turns implied etiquette into machine-checked preconditions.
6. Shared return types and shared state names do not guarantee shared semantics
- Two paths can both call
isSolvent(), both touchtotalBorrow, or both talk about thesamepending order while still asking different economic questions. - Reviewers should compare when state is read, which fields are consumed, and whether the call is proving pre-state comfort or post-state validity.
- Platypus and Abracadabra both looked superficially integrated with the main accounting model while actually preserving weaker helper-specific semantics.
What sound design looks like
-
Reachability, publication, and downstream spending authority stay separate on purpose
- A helper being callable does not by itself mean it may publish a canonical post-action state story, and helper-local publication still does not by itself mean later borrow, liquidation, migration, or reserve-spend paths may cash out against it.
- Healthy systems force those promotions to be explicit instead of letting a secondary path inherit them ambiently from the main flow.
-
One internal mutation-and-validate core
- Collateral release, debt updates, reserve movements, or entitlement consumption happen in one shared routine, with wrappers only changing UX behavior around it.
-
Helpers fail closed on uncertain architecture upgrades
- When a staking position becomes borrowable collateral, when a debt helper starts touching rebase state, or when a migration helper can now mint rights in a richer system, old wrappers revert until requalified.
-
Post-state validation, not pre-state reassurance
- The system proves the resulting state is valid after the helper’s economically relevant mutation has occurred.
-
Administrative sounding code treated as security-critical
- Bulk repayment, reserve sync, migration, rescue, and panic functions get the same invariant review as ordinary user-facing flows.
-
Fail-closed preconditions encoded in code, not docs
- If a helper is only safe when debt is zero, base and elastic are synchronized, or one entitlement object has not already been consumed elsewhere, that condition is enforced directly.
Useful positive design shapes
1. Shared economic-finality core
- Keep one internal function that mutates the economic object and validates the resulting state.
- Let
emergency,batch, andadminwrappers feed into that core instead of branching around it. - This is the most direct defense against helper-path drift.
2. Upgrade-triggered helper requalification
- Every architecture change that broadens what an object can back or settle should trigger a review of every old helper that can still move that object.
- The default answer should be
pause or narrow first, reopen after requalification, notassume old wrappers are still harmless.
3. Impossible-state assertions on aggregate accounting
- Helpers that touch aggregate debt, reserve, or supply rebase state should assert that amount/share pairs remain mutually meaningful after execution.
- If
elastic == 0 && base > 0or similar impossible combinations appear, later user-facing flows should stop rather than keep interpreting corrupted state.
4. Helper-specific permissions narrower than core-path permissions
- If a helper exists only for exceptional situations, its authority should be narrower than the normal path, not broader.
- Emergency and rescue code should usually remove options, not create fresh ways to bypass economic checks.
5. Optimistic windows need sibling-path exclusion, not just a final check
- Some helpers intentionally permit a temporarily invalid intermediate state because they expect to restore solvency or accounting coherence before returning.
- That pattern only survives if sibling entrypoints that can consume, liquidate, refinance, or otherwise rewrite the same economic object are excluded until the optimistic window closes.
- A final
isHealthy()check is not a safety boundary if liquidation, debt erasure, or collateral-state rewrites can happen halfway through the helper.
Design questions worth asking first
- Which path is the canonical truth-producing state transition for this economic action?
- Does this helper reuse that core, or does it open-code a cousin with different ordering or guard logic?
- What assumptions did this helper make before the protocol added borrowing, collateral reuse, rebase accounting, liquidation side paths, or migration semantics?
- If the helper corrupts aggregate state, or temporarily externalizes collateral while debt is still live, what later canonical path will keep trusting that state as if nothing happened?
- If the helper is only safe under one narrow condition, why is that condition not an explicit revert?
Red flags that usually mean the helper has become an unsupervised control surface
This path is just for emergenciesThis is only a bulk-management convenience functionThe normal flow checks that alreadyUsers are not supposed to call this while X is liveThe helper returns enough information if callers use it correctlyThis code predates borrowing / migration / collateral reuse, but should still be fine
Failure-prevention checklist
- Identify one canonical mutation-and-validate core for every economically meaningful action: collateral release, debt update, reserve movement, entitlement consumption, migration certification, or adapter-side collateral retirement.
- Force helpers, bulk utilities, rescue paths, and emergency wrappers to reuse that core or revert. If a path cannot cleanly reuse the core yet, treat that as a reason to fail closed, not a reason to open-code a weaker cousin.
- Requalify legacy helpers whenever architecture changes broaden what an object can back or settle: borrowing, collateral reuse, rebasing debt state, async order adapters, migration receipts, or cross-module closeout semantics.
- Encode helper-safe preconditions directly in code: debt-zero, synchronized amount/share pairs, unconsumed entitlement state, no pending-order value still counted elsewhere, no sibling liquidation path that can observe deliberately incomplete state, and no cross-module ledger expecting the same object to remain live.
- Add impossible-state assertions at aggregate-ledger boundaries so corrupted
base/elastic, reserve, share, or entitlement states halt later user paths instead of becoming new canonical truth. - Narrow helper permissions relative to the normal path. Exceptional wrappers should remove optionality and authority, not gain broader ability to move value or bypass validation.
- Treat administrative-sounding helpers as security-critical surfaces in review, monitoring, and incident response.
sync,sweep,migrate,donate,repayForAll, andrescuecan all be balance-sheet code. - Adversarially test helper sequencing under feature-expanded architecture, not only under the assumptions that existed when the helper was first written.
Audit prompts
- What is the single canonical truth-producing state transition for this economic action, and where does each helper diverge from its ordering, guard set, or state interpretation?
- Which helpers were written before borrowing, collateral reuse, migration certification, async adapters, or rebasing debt semantics existed, and what explicit requalification happened afterward?
- Can any helper mutate collateral, reserves, aggregate debt, liquidation eligibility, entitlement identity, or pending-order state without reusing the exact same post-state validation as the normal path?
- If a helper intentionally delays final health checks, which sibling entrypoints are still allowed to act on the same vault, position, or debt object before that check runs?
- If a helper corrupts aggregate state, which later user-facing flow will still price that state as canonical truth and cash it out into real value?
- What explicit revert protects the helper’s narrow safe state, and which safety assumptions are still being outsourced to caller etiquette or operator memory?
- Are emergency, rescue, bulk, or admin wrappers actually narrower than the canonical path, or do they secretly gain broader mutation authority because they skip normal sequencing?
- Do helper return values or shared state names create false comfort by looking semantically aligned with the main path while actually proving a weaker economic claim?
- If the protocol were upgraded tomorrow to let the touched object back one more module, would this helper fail closed automatically or remain live under stale assumptions?
Lessons that should already be standard
- Convenience code that moves economic state is not peripheral code; it is another finality boundary until proven otherwise.
Reachable helper,helper-authored state counts as canonical, andlater paths may cash out against that stateare different authority claims. Treat them as separate promotions, not one feature flag.- A helper that cannot safely reuse the canonical core should usually be disabled, not allowed to preserve a looser local version of truth.
The main path checks thatis not a defense when the helper can still author state the main path later trusts.- Architecture changes silently promote old helpers into new security-critical surfaces; feature expansion is a reason to narrow authority first.
- Shared variable names and helper calls do not guarantee shared economic semantics; sequencing and post-state meaning are what matter.
- Administrative labels like
sync,rescue, orrepayForAllare camouflage, not scope limits. - Fail-closed discipline is an architectural posture: uncertainty about helper safety should shrink authority immediately instead of waiting for a future cleanup pass.
How to use this note with the exploit corpus
- Start here when the sharper design question is why a helper, legacy, emergency, or bulk-management path was allowed to define a weaker economic boundary than the canonical core path.
- Pair with debt-zero-emergency-exit-discipline when the concrete issue is emergency collateral release while debt is still live.
- Pair with solvency check bypass when the broader exploit family is unchecked balance-sheet mutation that manufactures bad debt.
- Pair with zero-share borrow inflation when the helper poisoned aggregate debt/share state rather than releasing collateral directly.
- Pair with exit-right-consumed-state-binding when sibling helper and ordinary paths may each be spending the same entitlement object differently.
Internal linkages
- Reserve-helper anchor: bad-debt exploit (2023-03-13).
- Strong emergency-path exemplar: Platypus Finance USP emergencyWithdraw solvency-check bypass (2023-02-16).
- Strong bulk-helper exemplar: [[../incidents/lending-liquidations/abracadabra-cauldron-v4-repayforall-base-elastic-desynchronization-borrow-inflation-2024-01-30|Abracadabra Cauldron V4
repayForAll()base/elastic desynchronization borrow inflation (2024-01-30)]]. - Optimistic-management helper exemplar: [[../incidents/callbacks-reentrancy/arcadia-finance-vaultmanagementaction-liquidation-reentrancy-drain-2023-07-10|Arcadia Finance
vaultManagementAction()liquidation reentrancy drain (2023-07-10)]]. - Migration-closeout helper exemplar: refund drain (2022-10-27).
- Asynchronous-adapter helper exemplar: Abracadabra GMX V2 Cauldron RouterOrder phantom-collateral self-liquidation drain (2025-03-25).
- Broader lending-accounting umbrella: solvency-check-bypass.
- Emergency-release companion: debt-zero-emergency-exit-discipline.
- Debt-rebase corruption companion: debt-share-desynchronization-zero-share-borrow-inflation.
- Adjacent helper-consumption companion: exit-right-consumed-state-binding.
Control surface
-
The decisive control surface is every non-canonical path that can still move collateral, mutate aggregate debt state, alter reserve accounting, extract value from asynchronous adapter state, consume rights, or otherwise change the objects the main protocol later prices as truth.
-
The sharper review split is three-layered: which side paths are still reachable at all, which of those paths are still allowed to publish canonical completion or balance-sheet meaning, and which later spend paths still honor that helper-authored meaning as if the real economic boundary had already been crossed.
-
Healthy systems treat helper paths as alternative finality boundaries that must either reuse the real one or fail closed.
-
Sources:
- arcadia-finance-vaultmanagementaction-liquidation-reentrancy-drain-2023-07-10
- https://arcadiafinance.medium.com/post-mortem-72e9d24a79b0
- https://medium.com/@omniscia.io/platypus-finance-incident-post-mortem-7b71a0a47a5e
- https://www.certik.com/blog/platypus-finance-incident-analysis
- https://extropy-io.medium.com/breaking-down-the-6-5m-abracadabra-money-hack-a-detailed-analysis-of-the-cauldron-v4-exploit-44100da10896
- https://medium.com/@omniscia.io/euler-finance-incident-post-mortem-1ce077c28454
-
Last reviewed: 2026-08-02 UTC