DEUS DAO DEI burnFrom() allowance-axis inversion / approval-synthesis drain (2023-05-05)
- Name: DEUS DAO DEI
burnFrom()allowance-axis inversion / approval-synthesis drain - Date: 2023-05-05
- Protocol: DEUS DAO / DEI Stablecoin
- Chain: Arbitrum, BNB Chain, Ethereum
- Category: approval synthesis / allowance-axis inversion / delegated-burn authorization bug / multi-chain wallet drain
- Loss: about
$6.5Mtotal, with public reporting attributing roughly$5Mon Arbitrum,$1.3Mon BNB Chain, and about$135kon Ethereum; DEI also depegged by more than80% - Summary: DEUS upgraded its DEI stablecoin contract with a subtly broken
burnFrom()implementation. In a normal ERC-20 burnable token, delegated burn should consumeallowance[account][msg.sender]: the victim authorizes the caller. DEI instead readallowance[msg.sender][account], the reverse direction, then wrote the post-burn allowance back under the normal direction. That meant an attacker could first approve a victim address to spend the attacker’s own worthless-to-them DEI allowance, then callburnFrom(victim, 0). The zero-amount burn read the attacker-seeded allowance from the reversed slot and crystallized it into a realallowance[victim][attacker]. After that, a normaltransferFrom(victim, attacker, ...)drained the victim’s DEI. Because the same buggy DEI implementation was deployed on Arbitrum, BNB Chain, and Ethereum, the exploit replayed cross-chain until contracts were paused and whitehats began front-running some theft paths. - Trigger / entry point:
- The vulnerable surface was the DEI token’s public
burnFrom(address account, uint256 amount)implementation on each affected chain deployment. - The DEI token upgrade introduced a business-logic error into delegated-burn allowance handling.
- Instead of using the standard ERC-20 burnable relation
allowance[account][msg.sender], the function loadedcurrentAllowancefrom_allowances[_msgSender()][account]. - The function then updated allowance through
_approve(account, _msgSender(), currentAllowance - amount), which wrote state back under the normal owner→spender direction. - Because
amount = 0was allowed, attackers could trigger the dangerous read/write mismatch without needing any pre-existing approval from the victim and without burning any of the victim’s tokens during the setup step.
- The vulnerable surface was the DEI token’s public
- Root cause:
- DEI stored delegated spend authority in an ordered relation, but
burnFrom()consumed that relation using the wrong principal order. - The bug was not a missing access-control modifier and not a generic router issue. It was a directional authorization error inside familiar ERC-20 allowance logic.
- Reading allowance from
msg.sender -> accountand writing it back asaccount -> msg.senderlet an attacker transform an approval they themselves granted to the victim into a forged approval granted by the victim to the attacker. - Zero-amount burns made the flaw especially dangerous because the allowance-synthesis step did not need to pay an economic cost or survive a meaningful token-balance change.
- DEI stored delegated spend authority in an ordered relation, but
- Exploit path:
- The attacker identified a victim wallet holding a large DEI balance on Arbitrum, BNB Chain, or Ethereum.
- From the attacker’s own wallet, they called
approve(victim, hugeAmount)on the DEI token, seeding_allowances[attacker][victim]with a large value they controlled. - They then called
burnFrom(victim, 0). - Inside
burnFrom(), the token incorrectly loadedcurrentAllowancefrom_allowances[_msgSender()][account], so it treated the attacker-seededallowance[attacker][victim]as if it were proof that the victim had authorized the attacker. - Because
amountwas0, the allowance check and subtraction succeeded without reducing that value. - The function then executed
_approve(account, _msgSender(), currentAllowance - amount), which wrote a fresh real allowance into_allowances[victim][attacker]. - With that forged victim-to-attacker approval now on-chain, the attacker called ordinary
transferFrom(victim, attacker, amount)and drained the victim’s DEI. - The same sequence was replayed across multiple holders and multiple chains until emergency response and whitehat competition reduced the remaining exposed balances.
- What the attacker needed to be true:
- The victim had to hold DEI on one of the affected chain deployments.
- The attacker needed to be able to call
approve()from their own wallet and then invoke publicburnFrom()with the victim’s address. - The token had to permit
burnFrom(account, 0)and still run the allowance update path. - The same implementation mistake had to exist on the chain deployment the attacker targeted.
- Victim balances needed to remain in-wallet long enough that the synthesized approval could be cashed out with
transferFrom()before the contracts were paused or funds were moved.
- Technical anatomy worth remembering:
- This was not an
already-approved protocol drains walletsbug like Bancor or Beanstalk. DEUS did something more pathological: it manufactured victim approval out of attacker-controlled approval state by confusing the owner/spender axis. - The exploit hinged on the mismatch between where
burnFrom()read allowance and where it wrote allowance back. - The zero-amount path is worth preserving because it stripped away everything except the authorization bug itself. No complex pool state, bridge context, or reentrancy window was required.
- The case is also a sharp reminder that hand-editing familiar OpenZeppelin-style allowance code is dangerous precisely because the logic looks ordinary at a glance.
- DEUS had already suffered two major DEI incidents in 2022. This 2023 incident came from a simpler but even more embarrassing place: a fresh upgrade to the token’s own delegated-burn surface.
- This was not an
- Representative transactions and addresses:
- Vulnerable Arbitrum DEI contract reported in incident analysis:
0xbc1b62db243b51dabcd9540473324f36e094ec55 - Attacker address on Arbitrum and Ethereum:
0x189cf534de3097c08b6beaf6eb2b9179dab122d1 - Example Arbitrum exploit transaction:
0xb1141785b7b94eb37c39c37f0272744c6e79ca1517529fec3f4af59d4c3c37ef - Example BNB Chain exploit / frontrun transaction:
0xde2c8718a9efd8db0eaf9d8141089a22a89bca7d1415d04c05ba107dc1a190c3 - BNB Chain frontrunner address that signaled intent to return funds:
0x5a647e376d3835b8f941c143af3eb3ddf286c474 - Example Ethereum exploit transaction:
0x6129dd42778345bc278822a7feadeacb933f5e56ce51114e686832ad239307a8 - Recovery multisig referenced by DEUS / incident reporting:
0xf8b5e2e99faea5a3416822123c8eca24309cab99
- Vulnerable Arbitrum DEI contract reported in incident analysis:
- Response / mitigation:
- DEUS acknowledged the incident, paused affected contracts, and circulated a recovery multisig for returned funds.
- Public reporting said the BNB Chain exploit path was front-run by a rescuer who signaled an intent to return funds.
- DEUS said it would not pursue legal action if funds were returned and offered a
20%bounty. - By
2023-05-08, DEUS said it had recovered nearly$5.5Mout of roughly$7Mat issue and emphasized that its newer V3 contracts were isolated from DEI. - The immediate code fix was conceptually trivial: restore the allowance lookup to the standard owner→spender direction before any delegated burn consumes or updates approval.
Pattern tags
- allowance-axis-confusion-approval-synthesis
- Delegated-burn helper became the approval-forgery seam:
burnFrom()was supposed to consume(owner, spender)truth, but instead read attacker-seededallowance[attacker][victim]and rewrote it into realallowance[victim][attacker] - Zero-amount state transition stripped the exploit to pure authorization corruption:
burnFrom(victim, 0)did not need to move victim balance or survive any economic side effect before manufacturing spend rights - Cross-chain replay multiplied a tiny local logic bug into a multi-deployment wallet drain: the same broken DEI implementation existed on Arbitrum, BNB Chain, and Ethereum, so the forged-approval path stayed portable until each chain was contained
- Familiar ERC-20 surface camouflaged a custody-critical upgrade: the dangerous code lived in a boring-looking burnable-token helper rather than in an exotic router, bridge, or vault subsystem
Why the exploit worked
- DEUS collapsed three different allowance facts into one vague
allowance existsstory. The attacker could createallowance[attacker][victim];burnFrom()then falsely promoted that attacker-controlled slot into victim-granted delegated-burn truth; and the helper finally let the newly writtenallowance[victim][attacker]become ordinary livetransferFrom()authority. The incident is useful precisely because those three promotions were all visible in one short function. - The dangerous surface was not
approve()alone and nottransferFrom()alone. The real boundary failure sat in the middle helper that was supposed to consume delegated authority safely. OnceburnFrom()forgot who the owner and spender were, it stopped being a spend-limiting helper and started acting like an approval-synthesis primitive. - The zero-amount path made the exploit operationally clean. Because
amount = 0still ran the mismatched allowance read/write logic, the attacker did not need to burn victim funds, manage slippage, or pay meaningful setup cost to manufacture approval. That made repeat exploitation across many holders and chains fast and mechanically simple. - This is why the incident is a stronger foil to Bancor- or Beanstalk-style approval drains than a cousin to them. Those protocols already had real spender rights and then exposed the wrong caller or helper surface; DEUS instead corrupted the ordered authorization relation itself and created spender rights the victim had never granted.
- The cross-chain replay dimension mattered because the custody object was wallet-held DEI behind one shared implementation mistake. Once the exploit proved out on one deployment, the same local authorization bug could be cashed out anywhere the token logic remained live, which turned a single bad helper into a parallel multi-chain revoke-and-pause race.
Detection and audit pivots
- Audit delegated-burn and delegated-transfer helpers as authorization-state machines, not as supply or bookkeeping helpers. The highest-signal question is not
does burnFrom revert without approval?butdoes every allowance read and write preserve the same owner/spender orientation under all branches, including zero-value paths? - Add negative tests for the exact DEUS shape: attacker calls
approve(victim, N), thenburnFrom(victim, 0), thentransferFrom(victim, attacker, x). If that sequence can ever leave behind spendable victim-facing allowance, the helper is manufacturing consent. - Diff every custom token upgrade against a known-good reference at the slot level. Reviewers should literally line up
allowance[owner][spender]reads and writes forapprove(),transferFrom(), andburnFrom()rather than relying on visual familiarity with OpenZeppelin-shaped code. - Treat zero-amount delegated verbs as first-class exploit cases during fuzzing and code review. DEUS shows why
nothing movedis often exactly when pure authorization corruption is easiest to trigger and hardest to notice. - Inventory repeated-chain deployments when one token-surface logic bug appears. If Arbitrum, BNB Chain, and Ethereum all run the same helper semantics, containment and user messaging have to assume immediate replay rather than a single-chain patch window.
- Review emergency controls on wallet-facing token logic, not just protocol escrows. Approval-synthesis bugs primarily threaten wallet-held balances, so pause speed, revoke messaging, and whitehat coordination matter as much as internal solvency containment once the bug escapes.
Why this incident matters
- It is one of the cleanest real-world examples of an allowance-axis bug turning a delegated-burn helper into an approval-forgery machine.
- The note is especially useful as a foil to approval-drain families like Bancor and Beanstalk: those incidents abused pre-existing protocol approval, while DEUS synthesized victim approval that never existed.
- It also captures a recurring operational lesson about upgrades to standard token surfaces: tiny edits to familiar code paths can be more dangerous than exotic bespoke machinery because reviewers assume the pattern is already understood.
Failure-prevention checklist
- Keep delegated-burn code path-equivalent to the canonical ERC-20 burnable pattern unless there is a documented reason to diverge. If a fork must exist, review the exact allowance read slot and the exact allowance write slot side by side before deployment.
- Make role orientation explicit in code and review notes:
owner = account,spender = msg.sender, andallowance[owner][spender]should remain the same security fact through every delegated path. - Treat
amount == 0as a first-class exploit case. A zero-value delegated burn should either be a strict no-op on allowance state or consume the same ordered relation as a nonzero burn; it should never rewrite approval semantics for free. - Add property tests asserting that no public delegated helper can start from attacker-controlled
allowance[attacker][victim]and end with spendableallowance[victim][attacker]state. - Differential-test any custom token upgrade against the upstream OpenZeppelin implementation or another known-correct reference for
approve(),transferFrom(), andburnFrom()semantics. - Treat stablecoin token upgrades as custody-critical changes even when the edit looks
small. Familiar ERC-20 logic is exactly where reviewers relax too early. - Prebuild an emergency response path for wallet-held assets behind token approvals: pause authority, kill-switch conditions, user messaging, and recovery multisig coordination should already exist before the upgrade goes live.
Audit prompts
- For every allowance read and write in
burnFrom(), what exact principal occupies each slot: who is the owner, who is the spender, and where is that preserved in code rather than assumed from naming? - Can any public delegated helper mutate allowance state on a zero-amount path, and if so, what invariant proves it cannot synthesize or rotate third-party approval?
- Does any helper read
allowance[A][B]but write backallowance[B][A]under normal success conditions, including no-op or zero-value branches? - Which tests compare the upgraded token’s delegated-burn semantics against the reference ERC-20 burnable implementation, and would those tests fail on the exact DEUS inversion?
- If a malicious user pre-approves a victim from their own wallet, can any later helper reinterpret that state as victim consent?
- What operational control would actually stop cross-chain replay once one deployment is found vulnerable: global pause, per-chain pause, UI warnings, or only public revocation appeals?
- Are repeated protocol families and prior incidents causing review fatigue around the token surface, especially when the team edits
boringcode during an upgrade?
Lessons that should already be standard
- Ordered authorization state is directional truth, not generic metadata.
allowance[owner][spender]is a security object, and swapping its axes even once can manufacture fake consent. - Delegated-burn code is spender code. If
burnFrom()touches allowance, it deserves the same paranoia astransferFrom(). - Zero-value calls are not harmless edge cases. They are often the shortest route to exposing pure authorization corruption because they remove all economic noise.
- Small edits to standard-library token code are high-risk precisely because they look routine. Reviewers tend to memorize the pattern and stop re-deriving what each line means.
- Cross-chain deployments multiply blast radius for logic bugs. Once the same broken token implementation exists on Arbitrum, BNB Chain, and Ethereum, the attacker gets replayable wallet-drain surfaces until every chain is contained.
- Protocol history matters operationally even when mechanics differ. A team that has already had repeated stablecoin incidents should treat even tiny token-surface upgrades as major-risk events, not housekeeping.
Internal linkages
- Primary recurring family: allowance-axis-confusion-approval-synthesis — DEUS is the canonical exemplar of directionally misread allowance state turning attacker-controlled approval into forged victim approval.
- Adjacent approval family: exposed-token-pull-helper-approval-rights-exposure — useful contrast case because Bancor and Beanstalk spent approvals that already existed, whereas DEUS first synthesized the approval relation itself.
- Adjacent approval family: arbitrary-call-token-approval-drain — same wallet-drain endpoint, but DEUS did not need arbitrary routing or generic external-call freedom.
- Positive-design companion: spender-authority-truth-security-object — the healthier question above the exploit is what one exact
(owner, spender, delegated verb, spend scope)object should have stayed coherent before any burn or transfer helper could mutate wallet authority. - Adjacent root-authenticity family: account-confusion-root-of-trust-substitution — both are authorization failures, but DEUS did not accept a counterfeit root object; it corrupted the direction of a genuine authorization object.
- Prior protocol history: deus-dao-dei-lender-vwap-call-data-spoof-oracle-manipulation-2022-04-28 — a mechanically separate DEI incident that matters mostly as evidence that DEUS’s stablecoin surface had already been through repeated exploit stress before this upgrade bug.
Control surface
-
The decisive leverage sat in one ordered state relation:
_allowances[owner][spender]. -
burnFrom()read that relation in one direction and wrote it back in the other. Once the token forgot which side was the owner and which side was the spender, attacker-controlled approval state could be promoted into victim-authorized spend rights. -
The emergency dimension mattered too: the same implementation flaw existed on multiple chains, so the control surface was not just contract code but pause coordination, user revocation speed, and how quickly responders could stop replay across parallel deployments.
-
Sources:
- https://rekt.news/deus-dao-r3kt
- https://blog.solidityscan.com/deus-dao-hack-analysis-24583fe87e51/
- https://quillaudits.medium.com/decoding-deus-dao-6-5-million-exploit-quillaudits-588bbecec61f
- https://www.certik.com/blog/stablecoin-stumble-the-code-bug-led-to-usd6-5-million-loss-on-deus-finance
- https://arbiscan.io/tx/0xb1141785b7b94eb37c39c37f0272744c6e79ca1517529fec3f4af59d4c3c37ef
- https://bscscan.com/tx/0xde2c8718a9efd8db0eaf9d8141089a22a89bca7d1415d04c05ba107dc1a190c3
- https://etherscan.io/tx/0x6129dd42778345bc278822a7feadeacb933f5e56ce51114e686832ad239307a8
-
Last reviewed: 2026-07-23 UTC