Acala iBTC/aUSD Deprecated DexSaving Reward-Rate Misconfiguration / Recursive Unbacked aUSD Mint (2022-08-14)

  • Name: Acala iBTC/aUSD deprecated DexSaving reward-rate misconfiguration / recursive unbacked aUSD mint
  • Date: 2022-08-14
  • Protocol: Acala
  • Chain: Acala (Polkadot parachain)
  • Category: parameter misconfiguration / deprecated reward path abuse / unbacked stablecoin issuance / recursive liquidity-loop amplifier
  • Loss: 3.022B aUSD erroneously minted; Acala later said about 2.97B was recovered or identified onchain, while public reporting put the value that escaped before shutdown in the low millions to roughly $10M
  • Summary: On 2022-08-14, Acala’s newly launched iBTC/aUSD LP incentive program accidentally activated a deprecated DexSaving rewards path that could mint unbacked aUSD from the Honzon / CDP treasury. The intended iBTC/aUSD LP rewards were INTR and ACA, but the live incentives pallet still contained accumulate_dex_saving(), which computed aUSD rewards as DexSavingRewardRates[pool] * stable-side liquidity for any DEX LP containing Acala’s stable currency. Because the new pool was an aUSD pair, the misconfigured rate turned ordinary claim_rewards calls by iBTC/aUSD LPs into direct aUSD issuance. Some LPs then added the error-minted aUSD back into the iBTC/aUSD pool and claimed again, so the pool’s own growing aUSD side became the base for the next round of unbacked issuance.
  • Trigger / entry point:
    • Acala launched an iBTC/aUSD pool on Acala Swap and publicly intended to reward LPs with INTR and ACA.
    • The entry point was not a custom attacker contract or an exotic runtime exploit; it was the normal LP rewards path in the incentives pallet.
    • LP contributors who had staked iBTC/aUSD LP shares called incentives.claim_rewards, expecting ordinary liquidity-mining payouts.
    • Instead, because DexSavingRewardRates had been misconfigured for the pool and the old DexSaving path was still live, those claims paid out large amounts of freshly issued aUSD.
  • Root cause:
    • Acala’s incentives pallet still exposed a deprecated DexSaving mechanism that was originally meant to distribute stablecoin rewards to certain aUSD-related DEX pools.
    • In the vulnerable code, accumulate_dex_saving(lp_currency_id, pool_id) split the LP token into its two underlying assets, checked whether either side equaled StableCurrencyId, then set dex_saving_reward_base to the pool’s current stable-side reserve.
    • It then computed dex_saving_reward_amount = DexSavingRewardRates[pool_id] * dex_saving_reward_base and called CDPTreasury::issue_debit(..., false) to issue that stable currency to the incentives vault without fresh backing.
    • The runtime accumulated these rewards automatically in on_initialize every AccumulatePeriod; on Acala, that period was MINUTES, i.e. one minute at 12-second blocks.
    • The code did contain a generic rate bound rate <= 1, but that only limited the damage per accumulation period to at most the pool’s full aUSD side. It did not stop explosive growth once error-minted aUSD itself was repeatedly re-added as liquidity.
    • Acala’s own incident report says the parameter should have remained 0 and that the code should have prevented the misconfiguration from taking effect at all. The eventual fix was simply to remove accumulate_dex_saving, update_dex_saving_rewards, and the DexSavingRewardRates storage entirely.
  • Exploit path:
    1. Acala launched the iBTC/aUSD pool and configured intended LP incentives in INTR and ACA, but a deprecated aUSD-saving reward parameter was also left nonzero for that LP pool.
    2. Because the LP token was an aUSD pair, accumulate_dex_saving() treated the pool as eligible for stable-side saving rewards and began issuing unbacked aUSD into the incentives vault on each accumulation period.
    3. iBTC/aUSD LP contributors called incentives.claim_rewards and received these aUSD error mints as if they were legitimate pool rewards.
    4. Some recipients swapped the newly minted aUSD into DOT, IBTC, INTR, and other assets through Acala Swap, immediately damaging aUSD pair ratios across the DEX.
    5. Some LPs also added the error-minted aUSD back into the same iBTC/aUSD pool, obtained more LP exposure, and claimed again, so the pool’s expanding aUSD reserve mechanically increased the base for later DexSaving reward issuance.
    6. Other traders then bought or routed the distorted aUSD inventory at dislocated prices, and some of the resulting assets were transferred to Moonbeam, Interlay, Astar, Polkadot, and centralized exchanges before emergency governance pauses fully stopped outflow.
  • What the attacker needed to be true:
    • The iBTC/aUSD LP pool had to be live and have staked LP shares eligible for incentives.claim_rewards.
    • DexSavingRewardRates for that PoolId::Dex(...) entry had to be nonzero.
    • The LP token had to contain Acala’s stable currency, so the deprecated DexSaving branch would treat the pool’s aUSD side as the reward base.
    • The chain had to remain operational long enough for at least one on_initialize accumulation and one or more reward claims / swaps before governance paused the relevant pallets and transfers.
    • No parameter safeguard or pallet-level isolation could block the misconfigured reward path before it started issuing aUSD.
  • Technical anatomy worth remembering:
    • This was not a smart-contract exploit in the usual overflow, reentrancy, or forged proof sense. The dangerous primitive was an operator-controlled onchain parameter wired into still-live deprecated code with mint authority.
    • The misconfigured path did not merely over-distribute prefunded rewards. It used issue_debit(..., false) to create new aUSD without backing, so the reward system itself became a stablecoin mint surface.
    • The recursive amplifier mattered. Once error-minted aUSD was re-added to the iBTC/aUSD pool, the pool’s own stable-side balance became the base for the next reward accumulation. The payout rail and the growth rail were the same object.
    • Acala’s response is analytically useful because it did not try to salvage the deprecated feature with tighter settings. The protocol removed the whole DexSaving branch, which is a strong sign that the real problem was letting old reward logic keep live mint authority at all.
  • Representative modules, functions, and trace anchors:
    • Vulnerable incentives code (historical Acala source): modules/incentives/src/lib.rs, especially update_dex_saving_rewards(...) and accumulate_dex_saving(...)
    • Runtime accumulation cadence: runtime/acala/src/lib.rs and runtime/acala/src/constants.rs, where AccumulatePeriod = MINUTES and SECS_PER_BLOCK = 12
    • User-facing payout call: incentives.claim_rewards
    • Removal / fix: Acala PR #2341 (Remove dex savings)
    • Acala trace counts from the incident report:
      • 3.022B aUSD total error mints
      • 2.96B traced to the original 16 LP contributor addresses
      • 12.38M aUSD traced to the top 35 significant recipient / linked addresses
      • remaining 52.068M traced in the full report
  • Response / mitigation:
    • Acala rapidly passed urgent governance votes to reset the bad parameter, pause the incentives pallet, halt the DEX, pause Honzon, restrict non-ACA transfers and XCM outflows, and freeze other sensitive surfaces while tracing funds.
    • The community later burned large amounts of recovered aUSD and pursued return / bounty processes for funds that had already moved to other chains or venues.
    • The permanent code fix was to remove the deprecated DexSaving logic and clear DexSavingRewardRates in a runtime upgrade.
    • Acala’s published roadmap after the incident emphasized parameter-safeguard mechanisms, pallet isolation, and better monitoring, which is exactly where the real control failure sat.

Pattern tags

  • Core recurring pattern: deprecation-without-deauthorization.
  • The preserved shape is sharper than generic misconfiguration: a deprecated rewards rail still held stablecoin mint authority, and one operator-set parameter let ordinary LP claims spend that authority.
  • This is also a balance-sheet publication incident. The protocol’s own incentives machinery was allowed to publish fresh aUSD into circulation and that publication was immediately honored by Acala Swap, XCM exits, and downstream recipients.
  • The recursive amplifier matters enough to remember separately: the same aUSD that the deprecated rail printed could be re-added to the iBTC/aUSD pool, which then raised the stable-side reserve used to calculate later DexSaving issuance.

Why the exploit worked

  • Acala did not merely leave dead code around. It left a deprecated accumulate_dex_saving() branch live inside the block-initialization rewards path, still able to call CDPTreasury::issue_debit(..., false) and therefore still able to author fresh aUSD.
  • The bad parameter did more than overpay a rewards budget. Because the reward base was the pool’s current stable-side reserve, the mis-set DexSavingRewardRates value turned normal iBTC/aUSD LP participation into direct stablecoin issuance.
  • The incident report makes the recursive loop explicit: some LPs claimed error-minted aUSD, added it back into the same pool, and then claimed again. That meant pool balance, reward base, and newly issued asset were one joined object rather than separate surfaces.
  • Acala’s emergency response also reveals the real security boundary. The team did not just tweak one number; it reset the parameter, paused the incentives pallet, paused the DEX and Honzon, restricted transfers and XCM outflows, and later removed the whole DexSaving feature. That is strong evidence that the dangerous object was a live mint rail, not a cosmetic rewards misconfiguration.
  • In other words, the protocol had already socially decided the feature was legacy, but the runtime had not actually deauthorized it. The exploit path existed because deprecated and unable to mint balance-sheet assets were still different states.

Detection and audit pivots

  • Trace the full authority chain from operator-set reward parameters to balance-sheet issuance: parameter write -> deprecated accumulator -> treasury issuance -> claimable rewards -> swap / exit path. If that chain ends in money-good assets or bridgeable value, it is not a harmless incentives setting.
  • Ask whether deprecated code can still publish new assets, new debt, or new collateral-recognition state even when product owners believe the feature is retired. If yes, decommissioning has not actually happened.
  • Review any reward formula that depends on live pool balances or other attacker-recyclable state. If the reward asset can be reintroduced into the same base used for later reward computation, recursive self-amplification is already part of the threat model.
  • Inspect on_initialize and other automated accumulation hooks with the same skepticism usually reserved for user-callable functions. A time-based background path with mint authority is still an externally monetizable entrypoint once users can claim or route the resulting balances.
  • Separate rate bounded from safe. Acala’s rate <= 1 ceiling only limited one period’s issuance relative to the current pool balance; it did not stop the current pool balance itself from being recursively inflated by prior error mints.
  • Re-audit every path that can turn protocol-authored accounting objects into immediately transferable or swappable assets. Once Acala Swap and XCM exits honored the printed aUSD, the incentives bug had already become a systemwide solvency event.

Failure-prevention checklist

  • Extinguish deprecated rails, do not merely mark them unused. If a legacy reward path can still mint, issue debt, or publish collateral truth, remove it or hard-disable it before new programs go live.
  • Put hard semantic bounds on risk parameters, not just numeric bounds. A setting that can redirect a rewards path into stablecoin issuance should default to impossible unless a narrowly scoped governance action explicitly enables it.
  • Treat block-initialization reward accumulation as a minting surface. Subject on_initialize issuance paths to the same threat modeling, caps, and blast-radius controls as user-triggered mint functions.
  • Require recursive-amplification review whenever rewards depend on live balances. If the emitted asset can be re-added to the state that defines the next reward base, the formula needs explicit anti-recursion safeguards or hard issuance caps.
  • Keep new reward programs separated from legacy mint authority until rollout review proves the two cannot interact. Legacy feature present but set to zero is not an adequate safety claim.
  • Add automatic invariant checks for unexpected supply growth from incentives and for reward asset equals stable-side reserve asset. Those conditions should page operators before users can run repeated claim / swap / re-liquify loops.
  • Predefine degraded-mode responses for balance-sheet publication mistakes: pause claiming, pause swap venues, constrain bridge / XCM exits, and freeze only the surfaces that let newly printed claims escape into stronger assets.
  • Prefer pallet-level and feature-level authority isolation. A liquidity-mining pallet should not quietly inherit the power to mint system-stable liabilities unless that power is deliberate, visible, and tightly bounded.
  • Audit retirement and migration plans explicitly. The review question should be what authority still survives here?, not just does anyone intend to use this feature?.
  • Test claim -> re-add liquidity -> claim again loops in simulation whenever reward math touches live reserves, LP balances, or treasury-issued assets.

Audit prompts

  • Which deprecated or little-used modules can still mint, issue debt, publish collateral value, or otherwise write balance-sheet truth?
  • What exact parameter writes can redirect a rewards path into stablecoin issuance, and why are those writes even available in production?
  • If a reward asset is paid from a live reserve-dependent formula, can that same asset be recycled into the reserve that sets the next reward amount?
  • Does the protocol distinguish legacy code still compiled from legacy code still authorized, or are those currently the same state?
  • What monitoring would detect incentives growth becoming unexpected base-asset supply growth within one accumulation period?
  • If a malformed reward payout begins today, what is the first bounded-loss control: claim pause, DEX pause, bridge pause, transfer restriction, issuance cap, or none?
  • Which automated hooks run without a user transaction but still create assets users can later claim or route out of the system?
  • Are numeric parameter bounds validating the real economic invariant, or only preventing one-step extremes while leaving recursive amplification untouched?
  • When a feature is called deprecated, where is the proof that its authority is extinguished rather than merely socially ignored?
  • If the newly issued asset were swapped out immediately into stronger assets, what mechanism would stop the error from becoming permanent bad collateral / reserve loss?

Lessons that should already be standard

  • Deprecated code is still live financial infrastructure if configuration can still route mint authority through it.
  • A rewards system that can issue balance-sheet assets is part of the solvency core, even if operators think of it as an auxiliary incentives feature.
  • Recursive balance-dependent rewards are dangerous whenever the paid asset can flow back into the balance that sets the next payout.
  • Rate cap exists is not a meaningful defense if the denominator or reward base can be attacker-amplified between periods.
  • Offchain operational intent does not matter once the runtime still honors the old authority path. We meant to pay INTR and ACA did not constrain what the live pallet could actually mint.
  • Emergency pauses across swaps, issuance paths, and exits are not overreactions when a protocol has accidentally published unbacked stablecoin claims; they are evidence that the whole claim-recognition chain shares one security boundary.
  • The right decommissioning question is not is this feature deprecated? but what money-moving authority still survives if a parameter is wrong tomorrow?.

Why this incident matters

  • Acala is a useful reminder that deprecated code is still production code if it retains mint authority and can still be reached by configuration.
  • The incident also preserves a specific recursive failure shape: reward issuance depended on live pool balance, and the newly issued reward asset could itself be recycled into that same pool to increase the next issuance round.
  • It is also a clean decommissioning failure: the real missing control was not monitor parameters better, but prove the deprecated reward rail no longer had authority to issue balance-sheet assets before operators treated it as legacy.
  • For the exploit corpus, this is a cleaner example than many generic misconfiguration stories because the exact control path is legible: parameter -> deprecated reward accumulator -> unbacked stable issuance -> claim_rewards -> DEX swaps / recursive re-liquidity.

Internal linkages

  • Lifecycle control frame: deprecation-without-deauthorization — the decisive mistake was not just bad parameter; it was letting a deprecated reward branch keep stablecoin mint authority at all.
  • Positive-design decommissioning companion: decommissioning-authority-extinguishment-discipline — the healthier retirement boundary was to extinguish or quarantine the old DexSaving mint rail before the new LP program went live, not merely to socially regard it as deprecated.
  • Positive-design operations companion: operator-experimentation-separation-from-live-balance-sheet-authority — the more reusable production lesson is that a deprecated parameter rail was still able to publish live reward and mint authority on the main balance sheet.
  • Closest reserve-drain frame: issuance-redemption-solvency-asymmetry — the immediate payout rail was not a formal redeem function, but the system still let one side print unbacked aUSD while other DEX pools and exit routes honored that claim against real liquidity.
  • Launch-state amplifier: fresh-market-initialization-failure — the misconfigured path became especially dangerous because it was attached to a freshly launched LP program before operators or market depth could absorb the mistake.
  • Operational cousin: LP overmint (2024-01-04) — both incidents show that offchain configuration and operator-set parameters are part of the live security boundary when they decide what the protocol may mint or accept as safe.

Control surface