# Broker

In this challenge we have a broker that accepts weth as a collateral and allows you to borrow a new magic token. To beat this level, bring down the funds of the broker.

For this challenge, we have deployed a UniswapV2Factory at address 0x98b0C32857bbf24Fc070d75CA65B428a3fc8FF49 and weth is deployed at 0x5e5184Ab4ecFcb75342dda1Ae3d655c8EbAf113e

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;

interface IUniswapV2Pair {
    function mint(address to) external returns (uint liquidity);

    function getReserves()
        external
        view
        returns (
            uint112 _reserve0,
            uint112 _reserve1,
            uint32 _blockTimestampLast
        );

    function swap(
        uint amount0Out,
        uint amount1Out,
        address to,
        bytes calldata data
    ) external;
}

interface ERC20Like {
    function transfer(address dst, uint qty) external returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint qty
    ) external returns (bool);

    function approve(address dst, uint qty) external returns (bool);

    function balanceOf(address who) external view returns (uint);
}

interface WETH9 is ERC20Like {
    function deposit() external payable;
}

// a simple overcollateralized loan bank which accepts WETH as collateral and a
// token for borrowing. 0% APRs
contract PartOneBroker {
    IUniswapV2Pair public pair;
    ERC20Like public constant weth =
        WETH9(0x5e5184Ab4ecFcb75342dda1Ae3d655c8EbAf113e);
    ERC20Like public token;

    mapping(address => uint256) public deposited;
    mapping(address => uint256) public debt;

    constructor(IUniswapV2Pair _pair, ERC20Like _token) public payable {
        pair = _pair;
        token = _token;
    }

    function rate() public view returns (uint256) {
        uint112 _weth;
        uint112 _tok;
        if (token < weth) {
            (_tok, _weth, ) = pair.getReserves();
        } else {
            (_weth, _tok, ) = pair.getReserves();
        }
        uint256 _rate = uint256(_tok / _weth);
        return _rate;
    }

    function safeDebt(address user) public view returns (uint256) {
        return (deposited[user] * rate() * 2) / 3;
    }

    // borrow some tokens
    function borrow(uint256 amount) public {
        debt[msg.sender] += amount;
        require(
            safeDebt(msg.sender) >= debt[msg.sender],
            "err: undercollateralized"
        );
        token.transfer(msg.sender, amount);
    }

    // repay your loan
    function repay(uint256 amount) public {
        debt[msg.sender] -= amount;
        token.transferFrom(msg.sender, address(this), amount);
    }

    // repay a user's loan and get back their collateral. no discounts.
    function liquidate(address user, uint256 amount) public returns (uint256) {
        debt[user] -= amount;
        token.transferFrom(msg.sender, address(this), amount);
        uint256 collateralValueRepaid = amount / rate();
        if (collateralValueRepaid > weth.balanceOf(address(this))) {
            collateralValueRepaid = weth.balanceOf(address(this));
        }
        weth.transfer(msg.sender, collateralValueRepaid);
        return collateralValueRepaid;
    }

    // top up your collateral
    function deposit(uint256 amount) public {
        deposited[msg.sender] += amount;
        weth.transferFrom(msg.sender, address(this), amount);
    }

    // remove collateral
    function withdraw(uint256 amount) public {
        deposited[msg.sender] -= amount;
        require(
            safeDebt(msg.sender) >= debt[msg.sender],
            "err: undercollateralized"
        );
        weth.transfer(msg.sender, amount);
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;

import "./PartOneBroker.sol";

contract PartOneToken {
    mapping(address => uint256) public balanceOf;
    mapping(address => bool) public dropped;
    mapping(address => mapping(address => uint256)) public allowance;
    uint256 public totalSupply = 1_000_000 gwei;
    uint256 public AMT = totalSupply;

    constructor() public {
        balanceOf[msg.sender] = totalSupply;
    }

    function approve(address to, uint256 amount) public returns (bool) {
        allowance[msg.sender][to] = amount;
        return true;
    }

    function transfer(address to, uint256 amount) public returns (bool) {
        return transferFrom(msg.sender, to, amount);
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        if (from != msg.sender) {
            allowance[from][to] -= amount;
        }
        balanceOf[from] -= amount;
        balanceOf[to] += amount;
        return true;
    }

    function airdrop() public {
        require(!dropped[msg.sender], "err: only once");
        dropped[msg.sender] = true;
        balanceOf[msg.sender] += AMT;
        totalSupply += AMT;
    }
}

interface IUniswapV2Factory {
    function createPair(
        address tokenA,
        address tokenB
    ) external returns (address pair);
}

contract SetupPartOne {
    WETH9 public constant weth =
        WETH9(0x5e5184Ab4ecFcb75342dda1Ae3d655c8EbAf113e);
    IUniswapV2Factory public constant factory =
        IUniswapV2Factory(0x98b0C32857bbf24Fc070d75CA65B428a3fc8FF49);
    PartOneToken public token;
    IUniswapV2Pair public pair;
    PartOneBroker public broker;

    uint256 constant DECIMALS = 1 gwei;
    uint256 totalBefore;

    // create and bootstrap the token/weth pool for borrowing against WETH
    constructor() payable public{
        require(msg.value == 50 gwei, "err: 50 gwei");
        weth.deposit{value: msg.value}();
        
        token = new PartOneToken();
        pair = IUniswapV2Pair(
            factory.createPair(address(weth), address(token))
        );
        broker = new PartOneBroker(pair, ERC20Like(address(token)));
        token.transfer(address(broker), 500_000 * DECIMALS);

        weth.transfer(address(pair), 25 gwei);
        token.transfer(address(pair), 500_000 * DECIMALS);

        pair.mint(address(this));


        weth.approve(address(broker), type(uint256).max);
        broker.deposit(25 gwei);

        broker.borrow(250_000 * DECIMALS);

        totalBefore =
            weth.balanceOf(address(broker)) +
            token.balanceOf(address(broker)) /
            broker.rate();
    }

    function isSolved() public view returns (bool) {
        return weth.balanceOf(address(broker)) < 5 gwei;
    }
}