// Contract: BlacklistAndTimeFilter
// Target function: function calculateSwapAmount(ERC20 inputToken, ERC20 outputToken, uint256 totalInputToken) public view returns(uint256 outputAmount, uint256 inputAmount)
));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// Unified library for interacting with native ETH and ERC20
// Design inspiration from Mooniswap
library UniERC20 {
    using SafeERC20 for ERC20;

    function isETH(ERC20 token) internal pure returns (bool) {
        return (address(token) == address(0));
    }

    function uniCheckAllowance(ERC20 token, uint256 amount, address owner, address spender) internal view returns (bool) {
        if(isETH(token)){
            return msg.value==amount;
        } else {
            return token.allowance(owner, spender) >= amount;
        }
    }

    function uniBalanceOf(ERC20 token, address account) internal view returns (uint256) {
        if (isETH(token)) {
            return account.balance-msg.value;
        } else {
            return token.balanceOf(account);
        }
    }

    function uniTransfer(ERC20 token, address to, uint256 amount) internal {
        if (amount > 0) {
            if (isETH(token)) {
                (bool success, ) = payable(to).call{value: amount}("");
                require(success, "Transfer failed.");
            } else {
                token.safeTransfer(to, amount);
            }
        }
    }

    function uniTransferFromSender(ERC20 token, uint256 amount, address sendTo) internal {
        if (amount > 0) {
            if (isETH(token)) {
                require(msg.value == amount, "Incorrect value");
                payable(sendTo).transfer(msg.value);
            } else {
                token.safeTransferFrom(msg.sender, sendTo, amount);
            }
        }
    }
}

// Optimized sqrt library originally based on code from Uniswap v2
library Sqrt {
    // y is the number to sqrt
    // x MUST BE > int(sqrt(y)). This is NOT CHECKED.
    function sqrt(uint256 y, uint256 x) internal pure returns (uint256) {
        unchecked {
            uint256 z = y;
            while (x < z) {
                z = x;
                x = (y / x + x) >> 1;
            }
            return z;
        }
    }

    function sqrt(uint256 y) internal pure returns (uint256) {
        unchecked {
            uint256 x = y / 6e17;
            if(y <= 37e34){
                x = y/2 +1;
            }
            return sqrt(y,x); 
        }
    }
}

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

library SafeAggregatorInterface {
    using SafeCast for int256;

    uint256 constant ONE_DAY_IN_SECONDS = 86400;

    function safeUnsignedLatest(AggregatorV3Interface oracle) internal view returns (uint256) {
        (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = oracle.latestRoundData();
        require((roundId==answeredInRound) && (updatedAt+ONE_DAY_IN_SECONDS > block.timestamp), "Oracle out of date");
        return answer.toUint256();
    }
}

/*
    This exchange interface implements the Matcha PLP API

    Also controls swapFee and approvalContract (to minimize gas)

    It must be created before the Pool contract
    because it gets passed to the Pool contract constructor.

    Then setPoolAddress should be called to link this contract and destroy ownership.
*/
contract ClipperExchangeInterface is ReentrancyGuard, Ownable {
    using Sqrt for uint256;
    using UniERC20 for ERC20;
    using SafeAggregatorInterface for AggregatorV3Interface;

    ClipperPool public theExchange;
    ApprovalInterface public approvalContract;

    uint256 public swapFee;
    uint256 constant MAXIMUM_SWAP_FEE = 500;
    uint256 constant ONE_IN_DEFAULT_DECIMALS_DIVIDED_BY_ONE_HUNDRED_SQUARED = 1e14;
    uint256 constant ONE_IN_TEN_DECIMALS = 1e10;
    uint256 constant ONE_HUNDRED_PERCENT_IN_BPS = 1e4;
    uint256 constant ONE_BASIS_POINT_IN_TEN_DECIMALS = 1e6;

    address constant MATCHA_ETH_SIGIL = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    address constant CLIPPER_ETH_SIGIL = address(0);
    address immutable myAddress;

    event Swapped(
        address inAsset,
        address outAsset,
        address recipient,
        uint256 inAmount,
        uint256 outAmount,
        bytes auxiliaryData
    );

    event SwapFeeModified(
        uint256 swapFee
    );

    modifier poolOwnerOnly() {
        require(msg.sender == theExchange.owner(), "Clipper: Only owner");
        _;
    }


    constructor(ApprovalInterface initialApprovalContract, uint256 initialSwapFee) {
        require(initialSwapFee < MAXIMUM_SWAP_FEE, "Clipper: Maximum swap fee exceeded");
        approvalContract = initialApprovalContract;
        swapFee = initialSwapFee;
        myAddress = address(this);
    }

    // This function should be called immediately after the pool is initialzied
    // It can only be called once because of renouncing ownership
    function setPoolAddress(address payable poolAddress) external onlyOwner {
        theExchange = ClipperPool(poolAddress);
        renounceOwnership();
    }

    function modifyApprovalContract(ApprovalInterface newApprovalContract) external poolOwnerOnly {
        approvalContract = newApprovalContract;
    }

    function modifySwapFee(uint256 newSwapFee) external poolOwnerOnly {
        require(newSwapFee < MAXIMUM_SWAP_FEE, "Clipper: Maximum swap fee exceeded");
        swapFee = newSwapFee;
        emit SwapFeeModified(newSwapFee);
    }

    // Used for deposits and withdrawals, but not swaps
    function invariant() public view returns (uint256) {
        (uint256 balance, uint256 M, uint256 marketWeight) = theExchange.findBalanceAndMultiplier(ERC20(CLIPPER_ETH_SIGIL));
        uint256 cumulant = (M*balance).sqrt()/marketWeight;
        uint i;
        uint n = theExchange.nTokens();
        while(i < n){
            ERC20 the_token = ERC20(theExchange.tokenAt(i));
            (balance, M, marketWeight) = theExchange.findBalanceAndMultiplier(the_token);
            cumulant = cumulant + (M*balance).sqrt()/marketWeight;
            i++;
        }
        // Divide to put everything on a 1e18 track...
        return (cumulant*cumulant)/ONE_IN_DEFAULT_DECIMALS_DIVIDED_BY_ONE_HUNDRED_SQUARED;
    }

    // Closed-form invariant swap expression
    // solves: (sqrt(Mx)/X + sqrt(Ny)/Y) == (sqrt(M(x+a)/X) + sqrt(N(y-b))/Y) for b
    function invariantSwap(uint256 x, uint256 y, uint256 M, uint256 N, uint256 a, uint256 marketWeightX, uint256 marketWeightY) internal pure returns(uint256) {
        uint256 Ma = M*a;
        uint256 Mx = M*x;
        uint256 rMax = (Ma+Mx).sqrt();
        // Since rMax >= rMx, we can start with a great guess
        uint256 rMx = Mx.sqrt(rMax+1);
        uint256 rNy = (N*y).sqrt();
        uint256 X2 = marketWeightX*marketWeightX;
        uint256 XY = marketWeightX*marketWeightY;
        uint256 Y2 = marketWeightY*marketWeightY;

        // multiply by X*Y to get: 
        if(rMax*marketWeightY >= (rNy*marketWeightX+rMx*marketWeightY)) {
            return y;
        } else {
            return (2*((XY*rNy*(rMax-rMx)) + Y2*(rMx*rMax-Mx)) - Y2*Ma)/(N*X2);
        }
    }