contract UniswapV2MakerBroker is MakerBrokerBase, IBrokerDelegate {

    address public wethTokenAddress;
    address public loopringDelegate;
    IUniswapV2Router01 public uniswapV2Router;

    mapping(address => address) public tokenToExchange;
    mapping(address => bool) public tokenToIsSetup;

    constructor(address _loopringDelegate, address _uniswapV2Router, address _wethTokenAddress) public {
        loopringDelegate = _loopringDelegate;
        wethTokenAddress = _wethTokenAddress;
        uniswapV2Router = IUniswapV2Router01(_uniswapV2Router);
    }

    function setupToken(address token) public {
        IERC20(token).safeApprove(loopringDelegate, uint(- 1));
        IERC20(token).safeApprove(address(uniswapV2Router), uint(- 1));
        tokenToIsSetup[token] = true;
    }

    function setupTokens(address[] calldata tokens) external {
        for (uint i = 0; i < tokens.length; i++) {
            setupToken(tokens[i]);
        }
    }

    function() external payable {
        revert("UniswapV2MakerBroker: NO_DEFAULT");
    }

    // --------------------------------
    // Loopring Broker Delegate

    function brokerRequestAllowance(BrokerData.BrokerApprovalRequest memory request) public returns (bool) {
        require(msg.sender == loopringDelegate, "UniswapV2MakerBroker: UNAUTHORIZED");
        require(tokenToIsSetup[request.tokenS], "UniswapV2MakerBroker: TOKEN_S_NOT_SETUP");

        for (uint i = 0; i < request.orders.length; i++) {
            require(request.orders[i].tokenRecipient == address(this), "UniswapV2MakerBroker: INVALID_TOKEN_RECIPIENT");
            require(request.orders[i].owner == owner, "UniswapV2MakerBroker: INVALID_ORDER_OWNER");
        }

        address[] memory intermediatePaths;
        if (request.orders[0].extraData.length == 0) {
            intermediatePaths = new address[](0);
        } else {
            intermediatePaths = abi.decode(request.orders[0].extraData, (address[]));
        }

        address[] memory path = new address[](intermediatePaths.length + 2);
        path[0] = request.tokenB;
        for (uint i = 0; i < intermediatePaths.length; i++) {
            path[i + 1] = intermediatePaths[i];
        }
        path[path.length - 1] = request.tokenS;

        // We don't need to do a "try-catch" here because UniswapV2 has sufficient error handling.
        uniswapV2Router.swapExactTokensForTokens(
            request.totalFillAmountB,
            request.totalRequestedAmountS,
            path,
            address(this),
            block.timestamp
        );

        // We don't need a fill report.
        return false;
    }

    function onOrderFillReport(BrokerData.BrokerInterceptorReport memory fillReport) public {
        // No op
    }

    /*
     * Balance is unknown as it is entirely dependent on the offer in Uniswap being filled.
     * Thus we send down a very large number to ensure the relay/matching engine believes
     * orders from this broker have enough of a balance for trades to be filled.
     */
    function brokerBalanceOf(address owner, address tokenAddress) public view returns (uint) {
        return uint(- 1);
    }

}
/*
 * Inherits Loopring's IBrokerDelegate and sources liquidity from Uniswap
 * when the Loopring protocol requests a token approval. Because the Loopring
 * protocol expects the taker order to precede maker orders, and non-brokered
 * transfers occur before before brokered transfers, it is guaranteed that this
 * broker contract will receive the necessary tokens to trade, right before it
 * sets the approval and the Loopring protocol transfers the tokens out. Thus,
 * liquidity can be sourced on-chain with no money down!
 */
function brokerRequestAllowance(BrokerData.BrokerApprovalRequest memory request) public returns (bool) {
    require(msg.sender == loopringDelegate, "UniswapV2MakerBroker: UNAUTHORIZED");
    require(tokenToIsSetup[request.tokenS], "UniswapV2MakerBroker: TOKEN_S_NOT_SETUP");

    for (uint i = 0; i < request.orders.length; i++) {
        require(request.orders[i].tokenRecipient == address(this), "UniswapV2MakerBroker: INVALID_TOKEN_RECIPIENT");
        require(request.orders[i].owner == owner, "UniswapV2MakerBroker: INVALID_ORDER_OWNER");
    }

    address[] memory intermediatePaths;
    if (request.orders[0].extraData.length == 0) {
        intermediatePaths = new address[](0);
    } else {
        intermediatePaths = abi.decode(request.orders[0].extraData, (address[]));
    }

    address[] memory path = new address[](intermediatePaths.length + 2);
    path[0] = request.tokenB;
    for (uint i = 0; i < intermediatePaths.length; i++) {
        path[i + 1] = intermediatePaths[i];
    }
    path[path.length - 1] = request.tokenS;

    // We don't need to do a "try-catch" here because UniswapV2 has sufficient error handling.
    uniswapV2Router.swapExactTokensForTokens(
        request.totalFillAmountB,
        request.totalRequestedAmountS,
        path,
        address(this),
        block.timestamp
    );

    // We don't need a fill report.
    return false;
}

// --------------------------------
// Loopring Broker Delegate