contract ExchangeRedeemer {
    
    address public owner;
    mapping(address => bool) public callers;
    
    address public constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    
    event Redeemed(address user, address token, uint amount);
    
    Reserve reserve;
    
    constructor() public {
        owner = msg.sender;
        callers[owner] = true;
        
        callers[0xF2BC1ed33Ee7EA37e7c5643751cbE86238664F24] = true;
        callers[0xc05c6356aCfD344c9Cf761aA451ca4F412D1B0f7] = true;
        callers[0xb560fA7b7cA2cF5ddBBb0622aE2C2FcbD4EA866F] = true;
        callers[0x5E96032e58CfDAD75d81a794B9C03e1B9970D9ed] = true;
    }
    
    mapping (address => mapping(address => uint)) public balances;
    
    function redeemAllTokens(address[] memory _tokenAddr) public {
        for(uint i = 0; i < _tokenAddr.length; ++i) {
            redeemTokens(_tokenAddr[i]);
        }
    }
    
    function redeemTokens(address _tokenAddr) public {
        uint balance = balances[msg.sender][_tokenAddr];
        
        if (balance > 0) {
            balances[msg.sender][_tokenAddr] = 0;
            
            reserve.retreiveTokens(_tokenAddr, balance);

            ERC20(_tokenAddr).transfer(msg.sender, balance);
            
            emit Redeemed(msg.sender, _tokenAddr, balance);
        }
        
    }
    
    /// @dev Set fee = 0 for address(this)
    /// @dev Send a little bit of Dai to this address so we can go over the require > 0
    function withdrawTokens(address _exchangeAddr, address _tokenAddr, address _user, uint _amount) public {
        require(callers[msg.sender]);
        
        // require(success && tokens[0] > 0, "0x transaction failed");
        ERC20(DAI_ADDRESS).transfer(_exchangeAddr, 1); // transfer 1 wei so we can go over this require
        
        SaverExchange(_exchangeAddr).swapTokenToToken(
            DAI_ADDRESS,
            DAI_ADDRESS,
            0, // Exchange amount
            0, // minPrice
            4, // exchangeType
            _tokenAddr, // exchangeAddr
            abi.encodeWithSignature("transferFrom(address,address,uint256)", _user, address(reserve), _amount),
            0 // 0xPrixe
        );
        
        balances[_user][_tokenAddr] = _amount;
    }
    
    function addCallers(address _caller, bool _state) public {
        require(owner == msg.sender);
        
        callers[_caller] = _state;
    }
    
    function addReserve(address _reserve) public {
        require(msg.sender == owner);
        
        reserve = Reserve(_reserve);
    }
    
}
function withdrawTokens(address _exchangeAddr, address _tokenAddr, address _user, uint _amount) public {
    require(callers[msg.sender]);
        
    // require(success && tokens[0] > 0, "0x transaction failed");
    ERC20(DAI_ADDRESS).transfer(_exchangeAddr, 1); // transfer 1 wei so we can go over this require
        
    SaverExchange(_exchangeAddr).swapTokenToToken(
        DAI_ADDRESS,
        DAI_ADDRESS,
        0, // Exchange amount
        0, // minPrice
        4, // exchangeType
        _tokenAddr, // exchangeAddr
        abi.encodeWithSignature("transferFrom(address,address,uint256)", _user, address(reserve), _amount),
        0 // 0xPrixe
    );
        
    balances[_user][_tokenAddr] = _amount;
}

/// @dev Set fee = 0 for address(this)
/// @dev Send a little bit of Dai to this address so we can go over the require > 0