contract EthWrapperForBucketLender
{
    using TokenInteract for address;

    // ============ Constants ============

    // Address of the WETH token
    address public WETH;

    // ============ Constructor ============

    constructor(
        address weth
    )
        public
    {
        WETH = weth;
    }

    // ============ Public Functions ============

    /**
     * Fallback function. Disallows ether to be sent to this contract without data except when
     * unwrapping WETH.
     */
    function ()
        external
        payable
    {
        require( // coverage-disable-line
            msg.sender == WETH,
            "EthWrapperForBucketLender#fallback: Cannot recieve ETH directly unless unwrapping WETH"
        );
    }

    /**
     * Allows users to send eth directly to this contract and have it be wrapped and sent to a
     * BucketLender to be lent for some margin position.
     *
     * @param  bucketLender  The address of the BucketLender contract to deposit money into
     * @param  beneficiary   The address that will retain rights to the deposit
     * @return               The bucket number that was deposited into
     */
    function depositEth(
        address bucketLender,
        address beneficiary
    )
        external
        payable
        returns (uint256)
    {
        uint256 amount = msg.value;

        require(
            amount != 0,
            "EthWrapperForBucketLender#depositEth: Cannot deposit zero amount"
        );

        // wrap the eth
        WETH9(WETH).deposit.value(amount)();
        assert(WETH.balanceOf(address(this)) >= amount);

        // ensure enough allowance
        if (WETH.allowance(address(this), bucketLender) == 0) {
            // approve for "unlimited amount". WETH9 leaves this value as-is when doing transferFrom
            WETH.approve(bucketLender, MathHelpers.maxUint256());
        }

        // deposit the tokens
        return BucketLender(bucketLender).deposit(beneficiary, amount);
    }

    /**
     * Allows users to send eth directly to this contract and have it be wrapped and sent to a
     * BucketLender to be lent for some margin position.
     *
     * @param  bucketLender  The address of the BucketLender contract to deposit money into
     * @return               The bucket number that was deposited into
     */
    function withdrawEth(
        address bucketLender,
        uint256[] buckets,
        uint256[] maxWeights
    )
        external
        returns (uint256, uint256)
    {
        address owedToken = BucketLender(bucketLender).OWED_TOKEN();
        address heldToken = BucketLender(bucketLender).HELD_TOKEN();
        require(
            owedToken == WETH,
            "EthWrapperForBucketLender: Cannot withdraw from non-WETH BucketLender"
        );

        // withdraw the weth
        (
            uint256 owedTokenAmount,
            uint256 heldTokenAmount
        ) = BucketLender(bucketLender).withdraw(
            buckets,
            maxWeights,
            msg.sender
        );

        // send all eth to msg.sender
        if (owedTokenAmount != 0) {
            WETH9(owedToken).withdraw(owedTokenAmount);
            msg.sender.transfer(owedTokenAmount);
        }

        // send all other tokens to msg.sender
        if (heldTokenAmount != 0) {
            heldToken.transfer(msg.sender, heldTokenAmount);
        }

        return (owedTokenAmount, heldTokenAmount);
    }
}
/**
 * @title EthWrapperForBucketLender
 * @author dYdX
 *
 * Takes ETH directly, wraps it, then sends it to a bucket lender on behalf of a user.
 */
function withdrawEth(
    address bucketLender,
    uint256[] buckets,
    uint256[] maxWeights
)
    external
    returns (uint256, uint256)
{
    address owedToken = BucketLender(bucketLender).OWED_TOKEN();
    address heldToken = BucketLender(bucketLender).HELD_TOKEN();
    require(
        owedToken == WETH,
        "EthWrapperForBucketLender: Cannot withdraw from non-WETH BucketLender"
    );

    // withdraw the weth
    (
        uint256 owedTokenAmount,
        uint256 heldTokenAmount
    ) = BucketLender(bucketLender).withdraw(
        buckets,
        maxWeights,
        msg.sender
    );

    // send all eth to msg.sender
    if (owedTokenAmount != 0) {
        WETH9(owedToken).withdraw(owedTokenAmount);
        msg.sender.transfer(owedTokenAmount);
    }

    // send all other tokens to msg.sender
    if (heldTokenAmount != 0) {
        heldToken.transfer(msg.sender, heldTokenAmount);
    }

    return (owedTokenAmount, heldTokenAmount);
}

/**
 * Allows users to send eth directly to this contract and have it be wrapped and sent to a
 * BucketLender to be lent for some margin position.
 *
 * @param  bucketLender  The address of the BucketLender contract to deposit money into
 * @return               The bucket number that was deposited into
 */