// File: /contracts/Oracle.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.6;

import "./LinkTokenReceiver.sol";
import "./interfaces/ChainlinkRequestInterface.sol";
import "./interfaces/OracleInterface.sol";
import "./interfaces/LinkTokenInterface.sol";
import "./interfaces/WithdrawalInterface.sol";
import "./vendor/Ownable.sol";
import "./vendor/SafeMathChainlink.sol";

/**
 * @title The Chainlink Oracle contract
 * @notice Node operators can deploy this contract to fulfill requests sent to them
 */
contract Oracle is
    ChainlinkRequestInterface,
    OracleInterface,
    Ownable,
    LinkTokenReceiver,
    WithdrawalInterface
{
    using SafeMathChainlink for uint256;

    uint256 public constant EXPIRY_TIME = 5 minutes;
    uint256 private constant MINIMUM_CONSUMER_GAS_LIMIT = 400000;
    // We initialize fields to 1 instead of 0 so that the first invocation
    // does not cost more gas.
    uint256 private constant ONE_FOR_CONSISTENT_GAS_COST = 1;

    LinkTokenInterface internal LinkToken;
    mapping(bytes32 => bytes32) private commitments;
    mapping(address => bool) private authorizedNodes;
    mapping(address => bool) private callbackAddressAllowed;
    uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST;

    event OracleRequest(
        bytes32 indexed specId,
        address requester,
        bytes32 requestId,
        uint256 payment,
        address callbackAddr,
        bytes4 callbackFunctionId,
        uint256 cancelExpiration,
        uint256 dataVersion,
        bytes data
    );

    event CancelOracleRequest(bytes32 indexed requestId);

    modifier onlyCallbackAddressAllowed(address _callbackAddress) {
        require(
            callbackAddressAllowed[_callbackAddress],
            "Your APIComsumer contract is not allowed"
        );
        _;
    }

    /**
     * @notice Deploy with the address of the LINK token
     * @dev Sets the LinkToken address for the imported LinkTokenInterface
     * @param _link The address of the LINK token
     */
    constructor(address _link) public Ownable() {
        LinkToken = LinkTokenInterface(_link); // external but already deployed and unalterable
    }

    /**
     * @notice Creates the Chainlink request
     * @dev Stores the hash of the params as the on-chain commitment for the request.
     * Emits OracleRequest event for the Chainlink node to detect.
     * @param _sender The sender of the request
     * @param _payment The amount of payment given (specified in wei)
     * @param _specId The Job Specification ID
     * @param _callbackAddress The callback address for the response
     * @param _callbackFunctionId The callback function ID for the response
     * @param _nonce The nonce sent by the requester
     * @param _dataVersion The specified data version
     * @param _data The CBOR payload of the request
     */
    function oracleRequest(
        address _sender,
        uint256 _payment,
        bytes32 _specId,
        address _callbackAddress,
        bytes4 _callbackFunctionId,
        uint256 _nonce,
        uint256 _dataVersion,
        bytes calldata _data
    )
        external
        override
        onlyLINK
        checkCallbackAddress(_callbackAddress)
        onlyCallbackAddressAllowed(_callbackAddress)
    {
        bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce));
        require(commitments[requestId] == 0, "Must use a unique ID");
        // solhint-disable-next-line not-rely-on-time
        uint256 expiration = now.add(EXPIRY_TIME);

        commitments[requestId] = keccak256(
            abi.encodePacked(
                _payment,
                _callbackAddress,
                _callbackFunctionId,
                expiration
            )
        );

        emit OracleRequest(
            _specId,
            _sender,
            requestId,
            _payment,
            _callbackAddress,
            _callbackFunctionId,
            expiration,
            _dataVersion,
            _data
        );
    }

    /**
     * @notice Called by the Chainlink node to fulfill requests
     * @dev Given params must hash back to the commitment stored from `oracleRequest`.
     * Will call the callback address' callback function without bubbling up error
     * checking in a `require` so that the node can get paid.
     * @param _requestId The fulfillment request ID that must match the requester's
     * @param _payment The payment amount that will be released for the oracle (specified in wei)
     * @param _callbackAddress The callback address to call for fulfillment
     * @param _callbackFunctionId The callback function ID to use for fulfillment
     * @param _expiration The expiration that the node should respond by before the requester can cancel
     * @param _data The data to return to the consuming contract
     * @return Status if the external call was successful
     */
    function fulfillOracleRequest(
        bytes32 _requestId,
        uint256 _payment,
        address _callbackAddress,
        bytes4 _callbackFunctionId,
        uint256 _expiration,
        bytes calldata _data
    )
        external
        override
        onlyAuthorizedNode
        isValidRequest(_requestId)
        onlyCallbackAddressAllowed(_callbackAddress)
        returns (bool)
    {
        bytes32 paramsHash = keccak256(
            abi.encodePacked(
                _payment,
                _callbackAddress,
                _callbackFunctionId,
                _expiration
            )
        );
        require(
            commitments[_requestId] == paramsHash,
            "Params do not match request ID"
        );
        withdrawableTokens = withdrawableTokens.add(_payment);
        delete commitments[_requestId];
        require(
            gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT,
            "Must provide consumer enough gas"
        );
        // All updates to the oracle's fulfillment should come before calling the
        // callback(addr+functionId) as it is untrusted.
        // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern
        (bool success, ) = _callbackAddress.call(
            abi.encodeWithSelector(_callbackFunctionId, _requestId, _data)
        ); // solhint-disable-line avoid-low-level-calls
        return success;
    }

    /**
     * @notice Use this to check if a node is authorized for fulfilling requests
     * @param _node The address of the Chainlink node
     * @return The authorization status of the node
     */
    function getAuthorizationStatus(address _node)
        external
        view
        override
        returns (bool)
    {
        return authorizedNodes[_node];
    }

    function isCallbackAddressAllowed(address _callbackAddress)
        external
        view
        returns (bool)
    {
        return callbackAddressAllowed[_callbackAddress];
    }

    /**
     * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow.
     * @param _node The address of the Chainlink node
     * @param _allowed Bool value to determine if the node can fulfill requests
     */
    function setFulfillmentPermission(address _node, bool _allowed)
        external
        override
        onlyOwner
    {
        authorizedNodes[_node] = _allowed;
    }

    function addCallbackAddressToWhiteList(
        address _callbackAddress,
        bool _allowed
    ) external onlyOwner {
        callbackAddressAllowed[_callbackAddress] = _allowed;
    }

    /**
     * @notice Allows the node operator to withdraw earned LINK to a given address
     * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node
     * @param _recipient The address to send the LINK token to
     * @param _amount The amount to send (specified in wei)
     */
    function withdraw(address _recipient, uint256 _amount)
        external
        override(OracleInterface, WithdrawalInterface)
        onlyOwner
        hasAvailableFunds(_amount)
    {
        withdrawableTokens = withdrawableTokens.sub(_amount);
        assert(LinkToken.transfer(_recipient, _amount));
    }

    /**
     * @notice Displays the amount of LINK that is available for the node operator to withdraw
     * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage
     * @return The amount of withdrawable LINK on the contract
     */
    function withdrawable()
        external
        view
        override(OracleInterface, WithdrawalInterface)
        onlyOwner
        returns (uint256)
    {
        return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST);
    }

    /**
     * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK
     * sent for the request back to the requester's address.
     * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid
     * Emits CancelOracleRequest event.
     * @param _requestId The request ID
     * @param _payment The amount of payment given (specified in wei)
     * @param _callbackFunc The requester's specified callback address
     * @param _expiration The time of the expiration for the request
     */
    function cancelOracleRequest(
        bytes32 _requestId,
        uint256 _payment,
        bytes4 _callbackFunc,
        uint256 _expiration
    ) external override {
        bytes32 paramsHash = keccak256(
            abi.encodePacked(_payment, msg.sender, _callbackFunc, _expiration)
        );
        require(
            paramsHash == commitments[_requestId],
            "Params do not match request ID"
        );
        // solhint-disable-next-line not-rely-on-time
        require(_expiration <= now, "Request is not expired");

        delete commitments[_requestId];
        emit CancelOracleRequest(_requestId);

        assert(LinkToken.transfer(msg.sender, _payment));
    }

    /**
     * @notice Returns the address of the LINK token
     * @dev This is the public implementation for chainlinkTokenAddress, which is
     * an internal method of the ChainlinkClient contract
     */
    function getChainlinkToken() public view override returns (address) {
        return address(LinkToken);
    }

    // MODIFIERS

    /**
     * @dev Reverts if amount requested is greater than withdrawable balance
     * @param _amount The given amount to compare to `withdrawableTokens`
     */
    modifier hasAvailableFunds(uint256 _amount) {
        require(
            withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST),
            "Amount requested is greater than withdrawable balance"
        );
        _;
    }

    /**
     * @dev Reverts if request ID does not exist
     * @param _requestId The given request ID to check in stored `commitments`
     */
    modifier isValidRequest(bytes32 _requestId) {
        require(commitments[_requestId] != 0, "Must have a valid requestId");
        _;
    }

    /**
     * @dev Reverts if `msg.sender` is not authorized to fulfill requests
     */
    modifier onlyAuthorizedNode() {
        require(
            authorizedNodes[msg.sender] || msg.sender == owner(),
            "Not an authorized node to fulfill requests"
        );
        _;
    }

    /**
     * @dev Reverts if the callback address is the LINK token
     * @param _to The callback address
     */
    modifier checkCallbackAddress(address _to) {
        require(_to != address(LinkToken), "Cannot callback to LINK");
        _;
    }
}

// File: /contracts/vendor/SafeMathChainlink.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when 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.
 */
library SafeMathChainlink {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "SafeMath: modulo by zero");
        return a % b;
    }
}

// File: /contracts/vendor/Ownable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be aplied to your functions to restrict their use to
 * the owner.
 *
 * This contract has been modified to remove the revokeOwnership function
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(
            newOwner != address(0),
            "Ownable: new owner is the zero address"
        );
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: /contracts/interfaces/WithdrawalInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

interface WithdrawalInterface {
    /**
     * @notice transfer LINK held by the contract belonging to msg.sender to
     * another address
     * @param recipient is the address to send the LINK to
     * @param amount is the amount of LINK to send
     */
    function withdraw(address recipient, uint256 amount) external;

    /**
     * @notice query the available amount of LINK to withdraw by msg.sender
     */
    function withdrawable() external view returns (uint256);
}

// File: /contracts/interfaces/OracleInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

interface OracleInterface {
    function fulfillOracleRequest(
        bytes32 requestId,
        uint256 payment,
        address callbackAddress,
        bytes4 callbackFunctionId,
        uint256 expiration,
        bytes calldata data
    ) external returns (bool);

    function getAuthorizationStatus(address node) external view returns (bool);

    function setFulfillmentPermission(address node, bool allowed) external;

    function withdraw(address recipient, uint256 amount) external;

    function withdrawable() external view returns (uint256);
}

// File: /contracts/interfaces/LinkTokenInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

interface LinkTokenInterface {
    function allowance(address owner, address spender)
        external
        view
        returns (uint256 remaining);

    function approve(address spender, uint256 value)
        external
        returns (bool success);

    function balanceOf(address owner) external view returns (uint256 balance);

    function decimals() external view returns (uint8 decimalPlaces);

    function decreaseApproval(address spender, uint256 addedValue)
        external
        returns (bool success);

    function increaseApproval(address spender, uint256 subtractedValue)
        external;

    function name() external view returns (string memory tokenName);

    function symbol() external view returns (string memory tokenSymbol);

    function totalSupply() external view returns (uint256 totalTokensIssued);

    function transfer(address to, uint256 value)
        external
        returns (bool success);

    function transferAndCall(
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (bool success);

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool success);
}

// File: /contracts/interfaces/ChainlinkRequestInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

interface ChainlinkRequestInterface {
    function oracleRequest(
        address sender,
        uint256 requestPrice,
        bytes32 serviceAgreementID,
        address callbackAddress,
        bytes4 callbackFunctionId,
        uint256 nonce,
        uint256 dataVersion,
        bytes calldata data
    ) external;

    function cancelOracleRequest(
        bytes32 requestId,
        uint256 payment,
        bytes4 callbackFunctionId,
        uint256 expiration
    ) external;
}

// File: /contracts/LinkTokenReceiver.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

abstract contract LinkTokenReceiver {
    bytes4 private constant ORACLE_REQUEST_SELECTOR = 0x40429946;
    uint256 private constant SELECTOR_LENGTH = 4;
    uint256 private constant EXPECTED_REQUEST_WORDS = 2;
    uint256 private constant MINIMUM_REQUEST_LENGTH =
        SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS);

    /**
     * @notice Called when LINK is sent to the contract via `transferAndCall`
     * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount`
     * values to ensure correctness. Calls oracleRequest.
     * @param _sender Address of the sender
     * @param _amount Amount of LINK sent (specified in wei)
     * @param _data Payload of the transaction
     */
    function onTokenTransfer(
        address _sender,
        uint256 _amount,
        bytes memory _data
    )
        public
        onlyLINK
        validRequestLength(_data)
        permittedFunctionsForLINK(_data)
    {
        assembly {
            // solhint-disable-next-line avoid-low-level-calls
            mstore(add(_data, 36), _sender) // ensure correct sender is passed
            // solhint-disable-next-line avoid-low-level-calls
            mstore(add(_data, 68), _amount) // ensure correct amount is passed
        }
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, ) = address(this).delegatecall(_data); // calls oracleRequest
        require(success, "Unable to create request");
    }

    function getChainlinkToken() public view virtual returns (address);

    /**
     * @dev Reverts if not sent from the LINK token
     */
    modifier onlyLINK() {
        require(msg.sender == getChainlinkToken(), "Must use LINK token");
        _;
    }

    /**
     * @dev Reverts if the given data does not begin with the `oracleRequest` function selector
     * @param _data The data payload of the request
     */
    modifier permittedFunctionsForLINK(bytes memory _data) {
        bytes4 funcSelector;
        assembly {
            // solhint-disable-next-line avoid-low-level-calls
            funcSelector := mload(add(_data, 32))
        }
        require(
            funcSelector == ORACLE_REQUEST_SELECTOR,
            "Must use whitelisted functions"
        );
        _;
    }

    /**
     * @dev Reverts if the given payload is less than needed to create a request
     * @param _data The request payload
     */
    modifier validRequestLength(bytes memory _data) {
        require(
            _data.length >= MINIMUM_REQUEST_LENGTH,
            "Invalid request length"
        );
        _;
    }
}