DysonVault / Thena Overnight LP Unwind MEV
Exploit Transactions
0xbac614f4d103939a9611ca35f4ec9451e1e98512d573c822fbff70fafdbbb5a0Victim Addresses
0x2836B64a39d5B73d8f534c9fd6c6ABD81df2beB7BSCLoss Breakdown
Similar Incidents
MineSTM LP-burn MEV drains USDT from STM liquidity
36%MineSTM / STMERC20 Inner Pair MEV Drain
32%AIZPT314 bonding-curve arbitrage drains BNB reserves via flash swap
31%Thena Strategy Public Unstake Drain
29%Multi-venue stablecoin/WETH MEV arbitrage on Ethereum mainnet
25%Morpho-Pendle flash-loan liquidation MEV captures undercollateralized spread
24%Root Cause Analysis
DysonVault / Thena Overnight LP Unwind MEV
1. Incident Overview TL;DR
On BSC block 39,684,703 (chainid 56), an unprivileged EOA 0x4CeD363484dfeBd0faB1b33C3eca0eDca44a346C sent a single transaction (0xbac614f4d103939a9611ca35f4ec9451e1e98512d573c822fbff70fafdbbb5a0) to its own orchestrator contract 0x00Db72390C1843De815ef635EE58Ac19b54AF4EF using selector 0x6053b202. The orchestrator first interacted with DysonVault 0x2836B64a39d5B73d8f534c9fd6c6ABD81df2beB7 and its strategy StrategyCommonSolidlyHybridPoolLPOvernight to harvest rewards, then called DysonVault.withdrawAll() to burn all vault shares it held and redeem the corresponding Thena USD+/USDT+ LP tokens from Pair 0x1561D9618dB2Dcfe954f5D51f4381fa99C8E5689. It subsequently unwound those LP tokens through a chain of public routers and pools into WBNB and finally executed WBNB.withdraw(52.008 BNB), paying the resulting BNB back to the EOA.
The root cause is a MEV-style, anyone-can-take (ACT) opportunity. A passive DysonVault strategy concentrates depositor TVL in a single Thena USD+/USDT+ LP position and allows any share holder to withdraw and freely trade the underlying LP in one transaction. Given the publicly accessible Thena and Overnight liquidity, an adversary can deterministically unwind its share of the vault’s LP exposure into BNB with strictly positive profit, extracting value from other LPs and swap counterparties while DysonVault and its strategy maintain correct accounting invariants.
2. Key Background
DysonVault on BSC (vault proxy 0x2836B64a39d5B73d8f534c9fd6c6ABD81df2beB7, implementation 0x6e668080FF8Fa5F606cDC200229d054Aa5B8Fb13) is an upgradeable ERC20 vault that holds a single LP token as its underlying “want” and delegates active management to a strategy contract. The vault defines total TVL as its own want balance plus the strategy’s reported balance, and vault shares represent proportional claims on this total. In simplified form:
// DysonVault.sol (implementation at 0x6e66...fb13)
function balance() public view returns (uint256) {
return want().balanceOf(address(this)) + IStrategyDystopia(strategy).balanceOf();
}
function withdraw(uint256 _shares) public nonReentrant {
uint256 r = (balance() * _shares) / totalSupply();
_burn(msg.sender, _shares);
uint256 b = want().balanceOf(address(this));
if (b < r) {
uint256 _withdraw = r - b;
strategy.withdraw(_withdraw);
uint256 _after = want().balanceOf(address(this));
uint256 _diff = _after - b;
if (_diff < _withdraw) {
r = b + _diff;
}
}
want().safeTransfer(msg.sender, r);
}
The attached strategy is StrategyCommonSolidlyHybridPoolLPOvernight (implementation 0xaFF18b43Dfb44d9b56C2B88e8569b3B0880C2a56), which manages a Solidly-style hybrid pool on Thena for Overnight’s USD+ and USDT+. It stakes LP tokens in GaugeV2 0x3877c2C3D75aE80f2Ed8E9d4d68e3C1BFc77e5A6, harvests emissions into an output token, swaps the output into underlying stablecoins, mints USD+/USDT+ via Overnight exchanges, and adds liquidity to Thena Pair 0x1561D9618dB2Dcfe954f5D51f4381fa99C8E5689. A core part of the strategy is:
// StrategyCommonSolidlyHybridPoolLPOvernight.sol (impl 0xaFF1...2a56), addLiquidity()
function addLiquidity() internal override {
uint256 outputBal = IERC20Upgradeable(output).balanceOf(address(this));
uint256 lp0Amt = outputBal / 2;
uint256 lp1Amt = outputBal - lp0Amt;
// swap output -> lpToken0 / lpToken1 via public router
// mint USD+ / USDT+ via Overnight
// add liquidity to Thena pair and receive LP (want)
ISolidlyRouter(dystRouter2).addLiquidity(
usdPlus,
usdtPlus,
stable,
lp0Bal,
lp1Bal,
1,
1,
address(this),
block.timestamp
);
}
This design means DysonVault depositors are collectively exposed to a single LP position in the Thena USD+/USDT+ pool, whose value is determined by AMM pricing, Overnight rebasing, and external market conditions. Any address holding vault shares can invoke withdraw() or withdrawAll() to pull its proportional share of the underlying LP out of the vault.
The adversary uses a custom orchestrator contract 0x00Db72390C1843De815ef635EE58Ac19b54AF4EF, deployed by EOA 0x4CeD...346C at tx 0xabc74eea51706837d3790427e119cee1ca490ad81c3ddcd03137a2d7ca702af6. The orchestrator exposes a function with selector 0x6053b202 and is called exclusively by the same EOA in the collected tx history. Its purpose is to bundle a strategy harvest, vault withdrawal, LP redemption via Thena Pair, and multi-hop swaps through public routers, culminating in a WBNB withdrawal to BNB for the EOA.
3. Vulnerability Analysis & Root Cause Summary
At the contract level, DysonVault and its strategy preserve a standard vault accounting invariant: at all times, the total underlying LP balance equals the vault’s idle LP plus the strategy’s LP balance, and each user’s claim on the underlying is proportional to their vault shares. There is no evidence of reentrancy, access-control failure, or mis-accounting in either DysonVault or StrategyCommonSolidlyHybridPoolLPOvernight.
The vulnerability arises at the system level. The vault strategy concentrates TVL in a single Thena USD+/USDT+ LP position and exposes that LP to any vault share holder via withdrawAll(). Given existing Thena/Overnight/Algebra liquidity and pricing, an adversary can construct a cyclic transaction that (a) begins and ends with only BNB in an unprivileged EOA and its orchestrator, (b) uses withdrawn LP as the only link to DysonVault, and (c) produces strictly positive net BNB profit by trading against public pools, with losses absorbed by other LPs and swap counterparties.
In the observed incident, the adversary executes exactly such a cycle: harvest → withdraw all DysonVault shares → redeem the resulting LP in Thena Pair → route the stablecoins through public routers into WBNB → withdraw WBNB into BNB. The vault and strategy invariants hold throughout, but the combined economic state of DysonVault depositors and Thena/Overnight LPs shifts to the adversary’s advantage, making this a MEV-style ACT opportunity rooted in protocol design rather than a localized smart contract bug.
4. Detailed Root Cause Analysis
4.1 Contract-level invariant
DysonVault’s implementation enforces a standard vault invariant:
- Let
balance_t = want(vault)_t + strategy.balanceOf_t. - For any user with
shares_t, the claim on underlying isuser_underlying_t = balance_t * shares_t / totalSupply_t.
The vault’s deposit(), earn(), withdraw(), and withdrawAll() functions, together with the strategy’s deposit() and withdraw(), preserve this invariant. In particular, withdrawals burn shares first, then request underlying LP from the strategy, and will only reduce the redeemed amount if the strategy cannot return the requested LP. There are no code paths that mint extra shares, transfer want to arbitrary addresses, or mis-report balanceOf(). Our inspection of DysonVault.sol and StrategyCommonSolidlyHybridPoolLPOvernight.sol confirms that, for the seed transaction, the vault and strategy logic behave as designed.
4.2 System-level economic invariant and breakpoint
At the system level (DysonVault depositors + Thena/Overnight LPs + the adversary cluster), a natural economic invariant is:
- Starting from pre-state σ_B at block 39,684,703, there should be no cyclic, publicly constructible transaction sequence that:
- begins and ends with only BNB held by an unprivileged adversary-related cluster,
- leaves the adversary with no additional DysonVault shares or LP exposure at the end of the transaction, and
- yields strictly positive BNB profit for the adversary without an offsetting BNB-denominated loss to other participants beyond explicit AMM trading fees.
Equivalently, the aggregate BNB-valued portfolio of (adversary cluster + DysonVault depositors + Thena/Overnight LPs) should be conserved up to fees across any single-transaction unwind of a vault LP position.
The breakpoint occurs when the adversary uses its DysonVault shares to access the underlying LP and then trades against public liquidity in a way that violates this system-level invariant, while leaving the vault’s local invariant intact.
4.3 Seed transaction mechanism
The seed transaction is:
- Chain: BSC (56)
- Tx:
0xbac614f4d103939a9611ca35f4ec9451e1e98512d573c822fbff70fafdbbb5a0 - From: EOA
0x4CeD363484dfeBd0faB1b33C3eca0eDca44a346C - To: Orchestrator
0x00Db72390C1843De815ef635EE58Ac19b54AF4EF - Input selector:
0x6053b202
The collected trace.cast.log for this transaction shows the following key steps:
SM Address: 0x6e668080ff8fa5f606cdc200229d054aa5b8fb13, caller:0x00db72390c1843de815ef635ee58ac19b54af4ef,target:0x2836b64a39d5b73d8f534c9fd6c6abd81df2beb7
├─ 0x2836B64a39d5B73d8f534c9fd6c6ABD81df2beB7::withdrawAll()
│ ├─ 0x6e668080FF8Fa5F606cDC200229d054Aa5B8Fb13::withdrawAll() [delegatecall]
│ │ ├─ StrategyCommonSolidlyHybridPoolLPOvernight::withdraw(17139886497691096)
│ │ ├─ GaugeV2::withdraw(17139886497691096)
...
│ │ ├─ emit Burn(sender: 0x00Db72390C1843De815ef635EE58Ac19b54AF4EF,
│ │ │ amount0: 15571186191039571180026,
│ │ │ amount1: 18734917395,
│ │ │ to: 0x00Db72390C1843De815ef635EE58Ac19b54AF4EF)
...
├─ WBNB::withdraw(52008000000000000000)
│ ├─ emit Withdrawal(src: 0x00Db72390C1843De815ef635EE58Ac19b54AF4EF,
│ │ wad: 52008000000000000000)
From this trace and the associated balance_diff.json, the concrete mechanism is:
-
Strategy harvest and preparation
The orchestrator first triggers a harvest path onStrategyCommonSolidlyHybridPoolLPOvernight, which realizes accrued rewards, routes them through Overnight exchanges, and adds liquidity to the Thena USD+/USDT+ pair. This step does not violate any local invariants but may change the composition and size of the LP position. -
Vault withdrawal of adversary’s shares
The orchestrator callsDysonVault.withdrawAll()on vault0x2836...beb7from its own address0x00Db...4EF. The vault, via its implementation, burns all shares held by the orchestrator and requests the corresponding amount of LP tokens (want) from the strategy. The strategy withdraws17139886497691096LP tokens from GaugeV2 and returns them to the vault, which then transfers those LP tokens to the orchestrator as the withdraw recipient. At this point:- The vault-level invariant
balance = want(vault) + strategy.balanceOf()still holds. - Other DysonVault depositors retain their proportional shares of the remaining LP exposure.
- The vault-level invariant
-
Thena Pair LP burn and reserve shift
Holding the LP tokens, the orchestrator interacts with Thena Pair0x1561...5689:- LP tokens are transferred from the orchestrator to the Pair contract.
- The call
Pair::burn(0x00Db...4EF)is executed, emitting aBurnevent where:amount0 = 15571186191039571180026(UsdPlusToken units),amount1 = 18734917395(paired-asset units),to = 0x00Db...4EF.
- The Pair reserves are updated to new values recorded in the trace and reflected in the UsdPlusToken and Pair balance deltas in
balance_diff.json. This step realizes the adversary’s share of the underlying stablecoin position and shifts the Thena pool’s reserves, with passive LPs bearing the opposite side of the trade.
-
Multi-hop swaps into WBNB
The orchestrator then routes the received stable balances through a fixed chain of public swap contracts:- Router/pool
0x51Bd5e6d3da9064D59BcaA5A76776560aB42cEb8, - an AlgebraPool reachable via router
0x327Dd3208f0bCF590A66110aCB6e5e6941A4EfA0, - additional routers
0x1b9a1120a17617D8eC4dC80B921A9A1C50Caef7d, 0xd99c7F6C65857AC913a8f880A4cb84032AB2FC5b,- and
0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE.
The trace shows multiple
Swapevents on these contracts, with large stablecoin amounts in and WBNB amounts out, leaving intermediate LPs and counterparties with less favorable token compositions. - Router/pool
-
WBNB withdrawal to BNB
Finally, the orchestrator callsWBNB.withdraw(52008000000000000000)on WBNB0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c. The WBNB contract reduces its internal balance by 52.008 BNB and sends the same amount of native BNB to the orchestrator, as shown in theWithdrawalevent and in the native balance deltas.
From balance_diff.json, the net result in BNB is:
{
"native_balance_deltas": [
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"delta_wei": "-52008000000000000000"
},
{
"address": "0x4ced363484dfebd0fab1b33c3eca0edca44a346c",
"before_wei": "1317724903000000000",
"after_wei": "53304321053000000000",
"delta_wei": "51986596150000000000"
}
]
}
Gas usage is 2,680,770 at 5 gwei (0.01340385 BNB). Thus:
- Gross BNB gained by the adversary EOA: 51.98659615 BNB.
- Gas cost: 0.01340385 BNB.
- Net BNB profit: 51.9731923 BNB.
This profit is obtained in a single transaction, without any new DysonVault shares or persistent LP exposure for the adversary at the end, and with all contract-level invariants satisfied. The “missing” BNB comes from the WBNB contract and, economically, from liquidity providers and counterparties in the stablecoin and WBNB pools that were traded against along the path.
5. Adversary Flow Analysis
The adversary-related cluster consists of:
- EOA:
0x4CeD363484dfeBd0faB1b33C3eca0eDca44a346C(sender, profit recipient, and deployer of orchestrator). - Orchestrator contract:
0x00Db72390C1843De815ef635EE58Ac19b54AF4EF(callee of0x6053b202that bundles the exploit path).
This cluster is minimal: no additional helper contracts are required, and all other contracts involved (DysonVault, strategy, Thena Pair and Gauge, Overnight exchanges, routers, WBNB) are public and permissionless.
The adversary’s execution flow in the seed transaction is:
-
Preparation (off-chain / prior state)
- The adversary has previously acquired DysonVault shares (e.g., via deposits) so that the orchestrator holds a non-trivial vault balance.
- The vault owner has already set the strategy to
StrategyCommonSolidlyHybridPoolLPOvernight, and the vault TVL is concentrated in the Thena USD+/USDT+ LP position.
-
Step 1 – Orchestrator call
- The EOA submits tx
0xbac6...bb5a0calling0x00Db...4EFwith selector0x6053b202. No access control or whitelist gates this call; it is a standard public transaction with 5 gwei gas price and sufficient gas limit.
- The EOA submits tx
-
Step 2 – Strategy harvest
- The orchestrator triggers a harvest on
StrategyCommonSolidlyHybridPoolLPOvernight, which claims accumulated rewards, swaps them into desired assets, mints Overnight USD+/USDT+, and adds liquidity back into the Thena pair. This increases or reshapes the vault’s LP exposure but does not directly realize profit for the adversary.
- The orchestrator triggers a harvest on
-
Step 3 – Vault withdrawAll
- The orchestrator calls
DysonVault.withdrawAll()from its own address, causing the vault to:- Compute the adversary’s share of
balance()based on its vault shares. - Burn all the orchestrator’s shares.
- Pull LP from the strategy and transfer the corresponding LP tokens (
17139886497691096units) to the orchestrator.
- Compute the adversary’s share of
- The orchestrator calls
-
Step 4 – LP redemption via Thena Pair::burn
- Using the LP tokens, the orchestrator interacts with Thena Pair
0x1561...5689:- LP is transferred to the Pair contract.
Pair::burn(0x00Db...4EF)is called, which burns the LP and sends out the underlying USD+/USDT+ balances to the orchestrator, while updating pool reserves.
- Using the LP tokens, the orchestrator interacts with Thena Pair
-
Step 5 – Multi-hop swaps to WBNB
- The orchestrator routes the stablecoins through a deterministic series of swaps across public routers and pools, ending in WBNB. The trace shows large positive
amount0In/amount1Outvalues for these swaps, indicating significant stable-to-WBNB conversion.
- The orchestrator routes the stablecoins through a deterministic series of swaps across public routers and pools, ending in WBNB. The trace shows large positive
-
Step 6 – WBNB withdrawal to BNB
- The orchestrator calls
WBNB.withdraw(52.008 BNB), which reduces WBNB’s internal balances and transfers 52.008 BNB to the orchestrator. After accounting for gas, the EOA ends the transaction with 51.9731923 BNB more than it started with, and with no remaining DysonVault shares or LP tokens from this cycle.
- The orchestrator calls
Because all contracts involved are public and unprivileged, and the orchestrator is merely a convenience wrapper, any actor with DysonVault shares in the same pre-state could deploy an equivalent orchestrator and replay this strategy. This satisfies the ACT adversary model: the opportunity is not tied to special permissions, only to access to shares and public liquidity.
6. Impact & Losses
From balance_diff.json and the seed trace:
- The adversary EOA’s native BNB balance increases from 1.317724903 BNB to 53.304321053 BNB, a gross gain of 51.98659615 BNB.
- WBNB (
0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c) loses 52.008 BNB viawithdraw, consistent with the WBNB contract semantics. - Gas costs for the seed tx are 0.01340385 BNB, leaving a net profit of 51.9731923 BNB for the adversary cluster.
The economic loss is distributed across:
- Thena and Algebra LPs whose pools were used to route stablecoins into WBNB and whose reserves shifted against them.
- Swap counterparties who traded into less favorable prices during the adversary’s large, orchestrated swaps.
- DysonVault depositors indirectly, via reduced vault TVL corresponding to the legitimately withdrawn shares (though the vault’s internal accounting remains correct and no depositor’s share is mis-accounted).
There is no on-chain evidence of insolvency in DysonVault, but the incident demonstrates that a passive LP vault design, combined with deep public AMM liquidity, can expose users to deterministic MEV extraction on their pooled position.
7. References
- Seed transaction (exploit):
- BSC tx
0xbac614f4d103939a9611ca35f4ec9451e1e98512d573c822fbff70fafdbbb5a0(EOA → orchestrator).
- BSC tx
- Adversary-related accounts:
- EOA:
0x4CeD363484dfeBd0faB1b33C3eca0eDca44a346C - Orchestrator:
0x00Db72390C1843De815ef635EE58Ac19b54AF4EF(deployed in tx0xabc74eea51706837d3790427e119cee1ca490ad81c3ddcd03137a2d7ca702af6).
- EOA:
- Victim and strategy contracts:
- DysonVault proxy:
0x2836B64a39d5B73d8f534c9fd6c6ABD81df2beB7(implementation at0x6e668080FF8Fa5F606cDC200229d054Aa5B8Fb13). - StrategyCommonSolidlyHybridPoolLPOvernight implementation:
0xaFF18b43Dfb44d9b56C2B88e8569b3B0880C2a56(proxy0x2b9BDa587ee04fe51C5431709afbafB295F94bB4). - Thena USD+/USDT+ Pair:
0x1561D9618dB2Dcfe954f5D51f4381fa99C8E5689. - GaugeV2 for the LP:
0x3877c2C3D75aE80f2Ed8E9d4d68e3C1BFc77e5A6. - UsdPlusToken proxy:
0xe80772Eaf6e2E18B651F160Bc9158b2A5caFCA65(implementation0x6002054688d62275d80cc615f0f509d9b2ff520d). - WBNB:
0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c.
- DysonVault proxy:
- Key logs and artifacts (for reproduction):
- Seed tx metadata and receipt for
0xbac6...bb5a0. - Seed tx
trace.cast.logshowingDysonVault.withdrawAll, strategy withdraw,Pair::burn, swap, andWBNB::withdrawcalls and events. balance_diff.jsonfor tx0xbac6...bb5a0, showing native and ERC20 balance changes used in the profit calculation.- Verified source code for DysonVault.sol, StrategyCommonSolidlyHybridPoolLPOvernight.sol, Thena Pair.sol, Router.sol, and WBNB.sol, which allows independent verification of the invariants and flow described above.
- Seed tx metadata and receipt for