contract UniswapRebalancerMakerBroker is MakerBrokerBase, IBrokerDelegate {

    address internal _wethTokenAddress;
    address internal _loopringDelegate;
    address internal _uniswapV2Router;
    uint8 internal _slippageFactor;

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

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

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

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

    function setSlippageFactor(uint8 slippageFactor) external onlyOwner {
        _slippageFactor = slippageFactor;
    }

    function getSlippageFactor() external view returns (uint8) {
        return _slippageFactor;
    }

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

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

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

        for (uint i = 0; i < request.orders.length; i++) {
            require(request.orders[i].tokenRecipient == address(this), "UniswapRebalancerMakerBroker: INVALID_TOKEN_RECIPIENT");
            require(request.orders[i].owner == owner, "UniswapRebalancerMakerBroker: 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[]));
        }

        if (intermediatePaths.length == 0 || intermediatePaths[0] != address(0x0000000000000000000000000000000000000001)) {
            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.
            IUniswapV2Router01(_uniswapV2Router).swapExactTokensForTokens(
                request.totalFillAmountB,
                request.totalRequestedAmountS / uint(_slippageFactor),
                path,
                address(this),
                block.timestamp
            );
        }

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

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

    /*
     * This broker isn't concerned with accurate balance reporting. It simplifies balance-tracking logic to just report
     * a maximum.
     */
    function brokerBalanceOf(address owner, address token) public view returns (uint) {
        return uint(- 1);
    }

    function balanceOf(address token) public view returns (uint) {
        return IERC20(token).balanceOf(address(this));
    }

}
/*
 * 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 brokerBalanceOf(address owner, address token) public view returns (uint) {
    return uint(- 1);
}

/*
 * This broker isn't concerned with accurate balance reporting. It simplifies balance-tracking logic to just report
 * a maximum.
 */