# DFX Finance: reentrancy in flash loan drains $4M

DFX Finance Curve pool reentrancy via flash loan callback let attacker deposit during flash, inflating withdrawal and draining ~$4M.

- Payout: Real-world hack
- Severity: critical
- Program: - DFXFinance
- Analysis by: DeFiHackLabs
- Disclosed: 2022-11-10
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-11/DFX_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20221110-dfx
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity vuln:flash-loan vuln:reentrancy

**Target:** Curve-style DFX pool contract with flash loan and deposit/withdraw
**Root cause:** The flash loan callback executes before state finalization, allowing reentrant deposit that manipulates the pool's accounting and inflates the attacker's withdrawal amount.
**Steps:**
1. Flash loan XIDR and USDC from DFX pool via `dfx.flash(address(this), XIDR_USDC[0]*995/1000, XIDR_USDC[1]*995/1000, new bytes(1))`.
2. In `flashCallback`, call `dfx.deposit(200_000 * 1e18, block.timestamp + 60)` to reenter and receive pool tokens.
3. After flash loan returns, call `dfx.withdraw(receiption, block.timestamp + 60)` to redeem inflated pool tokens.
4. Swap withdrawn XIDR to USDC via Uniswap V3 router and repay flash loan, keeping profit.
**Exploitation:** Attacker flash-borrowed pool assets, reentered deposit during callback to mint pool tokens at manipulated rate, then withdrew for more than deposited, converting to USDC for ~$4M profit.
**Insight:** Flash loan callbacks that allow state-changing calls before the loan is settled can be reentered to mint pool shares at stale prices; check that deposit/withdraw are locked during flash loans.

## Proof of concept (copied exactly from the original report)

**Flash loan call triggering reentrant deposit**

```
dfx.flash(address(this), XIDR_USDC[0] * 995 / 1000, XIDR_USDC[1] * 995 / 1000, new bytes(1)); // 5% fee
        dfx.withdraw(receiption, block.timestamp + 60);
```

**Reentrant deposit inside flash callback**

```
function flashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
        (receiption,) = dfx.deposit(200_000 * 1e18, block.timestamp + 60);
```

