contract TokenSpender is
    IFeature,
    ITokenSpender,
    FixinCommon
{
    // solhint-disable
    /// @dev Name of this feature.
    string public constant override FEATURE_NAME = "TokenSpender";
    /// @dev Version of this feature.
    uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
    /// @dev The implementation address of this feature.
    address private immutable _implementation;
    // solhint-enable

    using LibRichErrorsV06 for bytes;

    constructor() public {
        _implementation = address(this);
    }

    /// @dev Initialize and register this feature. Should be delegatecalled
    ///      into during a `Migrate.migrate()`.
    /// @param allowanceTarget An `allowanceTarget` instance, configured to have
    ///        the ZeroeEx contract as an authority.
    /// @return success `MIGRATE_SUCCESS` on success.
    function migrate(IAllowanceTarget allowanceTarget) external returns (bytes4 success) {
        LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
        ISimpleFunctionRegistry(address(this))
            .extend(this.getAllowanceTarget.selector, _implementation);
        ISimpleFunctionRegistry(address(this))
            .extend(this._spendERC20Tokens.selector, _implementation);
        ISimpleFunctionRegistry(address(this))
            .extend(this.getSpendableERC20BalanceOf.selector, _implementation);
        return LibMigrate.MIGRATE_SUCCESS;
    }

    /// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within.
    /// @param token The token to spend.
    /// @param owner The owner of the tokens.
    /// @param to The recipient of the tokens.
    /// @param amount The amount of `token` to transfer.
    function _spendERC20Tokens(
        IERC20TokenV06 token,
        address owner,
        address to,
        uint256 amount
    )
        external
        override
        onlySelf
    {
        IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget;
        // Have the allowance target execute an ERC20 `transferFrom()`.
        (bool didSucceed, bytes memory resultData) = address(spender).call(
            abi.encodeWithSelector(
                IAllowanceTarget.executeCall.selector,
                address(token),
                abi.encodeWithSelector(
                    IERC20TokenV06.transferFrom.selector,
                    owner,
                    to,
                    amount
                )
            )
        );
        if (didSucceed) {
            resultData = abi.decode(resultData, (bytes));
        }
        if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) {
            LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
                address(token),
                owner,
                to,
                amount,
                resultData
            ).rrevert();
        }
    }

    /// @dev Gets the maximum amount of an ERC20 token `token` that can be
    ///      pulled from `owner` by the token spender.
    /// @param token The token to spend.
    /// @param owner The owner of the tokens.
    /// @return amount The amount of tokens that can be pulled.
    function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
        external
        override
        view
        returns (uint256 amount)
    {
        return LibSafeMathV06.min256(
            token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
            token.balanceOf(owner)
        );
    }

    /// @dev Get the address of the allowance target.
    /// @return target The target of token allowances.
    function getAllowanceTarget()
        external
        override
        view
        returns (address target)
    {
        return address(LibTokenSpenderStorage.getStorage().allowanceTarget);
    }
}
/// @dev Feature that allows spending token allowances.
function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
    external
    override
    view
    returns (uint256 amount)
{
    return LibSafeMathV06.min256(
        token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
        token.balanceOf(owner)
    );
}

/// @dev Gets the maximum amount of an ERC20 token `token` that can be
///      pulled from `owner` by the token spender.
/// @param token The token to spend.
/// @param owner The owner of the tokens.
/// @return amount The amount of tokens that can be pulled.