contract FundingScript is ITokenRecipient {
    TBTCToken tbtcToken;
    VendingMachine vendingMachine;
    TBTCDepositToken tbtcDepositToken;
    FeeRebateToken feeRebateToken;

    constructor(
        address _VendingMachine,
        address _TBTCToken,
        address _TBTCDepositToken,
        address _FeeRebateToken
    ) public {
        vendingMachine = VendingMachine(_VendingMachine);
        tbtcToken = TBTCToken(_TBTCToken);
        tbtcDepositToken = TBTCDepositToken(_TBTCDepositToken);
        feeRebateToken = FeeRebateToken(_FeeRebateToken);
    }

    /// @notice Receives approval for a TDT transfer, and calls `VendingMachine.unqualifiedDepositToTbtc` for a user.
    /// @dev Implements the approveAndCall receiver interface.
    /// @param _from The owner of the token who approved them for transfer.
    /// @param _tokenId Approved TDT for the transfer.
    /// @param _extraData Encoded function call to `VendingMachine.unqualifiedDepositToTbtc`.
    function receiveApproval(
        address _from,
        uint256 _tokenId,
        address,
        bytes memory _extraData
    ) public { // not external to allow bytes memory parameters
        require(msg.sender == address(tbtcDepositToken), "Only token contract can call receiveApproval");

        tbtcDepositToken.transferFrom(_from, address(this), _tokenId);
        tbtcDepositToken.approve(address(vendingMachine), _tokenId);

        // Verify _extraData is a call to unqualifiedDepositToTbtc.
        bytes4 functionSignature;
        assembly {
            functionSignature := and(mload(add(_extraData, 0x20)), not(0xff))
        }
        require(
            functionSignature == vendingMachine.unqualifiedDepositToTbtc.selector,
            "Bad _extraData signature. Call must be to unqualifiedDepositToTbtc."
        );

        // Call the VendingMachine.
        // We could explictly encode the call to vending machine, but this would
        // involve manually parsing _extraData and allocating variables.
        // We capture the `returnData` in order to forward any nested revert message
        // from the contract call.
        /* solium-disable-next-line security/no-low-level-calls */
        (bool success, bytes memory returnData) = address(vendingMachine).call(_extraData);

        string memory revertMessage;
        assembly {
            // A revert message is ABI-encoded as a call to Error(string).
            // Slicing the Error() signature (4 bytes) and Data offset (4 bytes)
            // leaves us with a pre-encoded string.
            // We also slice off the ABI-coded length of returnData (32).
            revertMessage := add(returnData, 0x44)
        }

        require(success, revertMessage);

        // Transfer the TBTC and feeRebateToken to the user.
        tbtcToken.transfer(_from, tbtcToken.balanceOf(address(this)));
        feeRebateToken.transferFrom(address(this), _from, uint256(_tokenId));
    }
}
/// @notice A one-click script for minting TBTC from an unqualified TDT.
/// @dev Wrapper script for VendingMachine.unqualifiedDepositToTbtc
/// This contract implements receiveApproval() and can therefore use
/// approveAndCall(). This pattern combines TBTC Token approval and
/// vendingMachine.unqualifiedDepositToTbtc() in a single transaction.
function receiveApproval(
    address _from,
    uint256 _tokenId,
    address,
    bytes memory _extraData
) public { // not external to allow bytes memory parameters
    require(msg.sender == address(tbtcDepositToken), "Only token contract can call receiveApproval");

    tbtcDepositToken.transferFrom(_from, address(this), _tokenId);
    tbtcDepositToken.approve(address(vendingMachine), _tokenId);

    // Verify _extraData is a call to unqualifiedDepositToTbtc.
    bytes4 functionSignature;
    assembly {
        functionSignature := and(mload(add(_extraData, 0x20)), not(0xff))
    }
    require(
        functionSignature == vendingMachine.unqualifiedDepositToTbtc.selector,
        "Bad _extraData signature. Call must be to unqualifiedDepositToTbtc."
    );

    // Call the VendingMachine.
    // We could explictly encode the call to vending machine, but this would
    // involve manually parsing _extraData and allocating variables.
    // We capture the `returnData` in order to forward any nested revert message
    // from the contract call.
    /* solium-disable-next-line security/no-low-level-calls */
    (bool success, bytes memory returnData) = address(vendingMachine).call(_extraData);

    string memory revertMessage;
    assembly {
        // A revert message is ABI-encoded as a call to Error(string).
        // Slicing the Error() signature (4 bytes) and Data offset (4 bytes)
        // leaves us with a pre-encoded string.
        // We also slice off the ABI-coded length of returnData (32).
        revertMessage := add(returnData, 0x44)
    }

    require(success, revertMessage);

    // Transfer the TBTC and feeRebateToken to the user.
    tbtcToken.transfer(_from, tbtcToken.balanceOf(address(this)));
    feeRebateToken.transferFrom(address(this), _from, uint256(_tokenId));
}

/// @notice Receives approval for a TDT transfer, and calls `VendingMachine.unqualifiedDepositToTbtc` for a user.
/// @dev Implements the approveAndCall receiver interface.
/// @param _from The owner of the token who approved them for transfer.
/// @param _tokenId Approved TDT for the transfer.
/// @param _extraData Encoded function call to `VendingMachine.unqualifiedDepositToTbtc`.