# 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;
}
}