// File: AggregatorV3Interface.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface AggregatorV3Interface {

  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}

// File: HoldefiOwnable.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.12;

/// @title HoldefiOwnable
/// @author Holdefi Team
/// @notice Taking ideas from Open Zeppelin's Ownable contract
/// @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.
///
/// By default, the owner account will be the one that deploys the contract. This
/// can later be changed with {transferOwnership}.
///
/// This module is used through inheritance. It will make available the modifier
/// `onlyOwner`, which can be applied to your functions to restrict their use to
/// the owner.
/// @dev Error codes description: 
///     OE01: Sender should be the owner
///     OE02: New owner can not be zero address
///     OE03: Pending owner is empty
///     OE04: Pending owner is not same as the `msg.sender`
contract HoldefiOwnable {
    address public owner;
    address public pendingOwner;

    /// @notice Event emitted when an ownership transfer request is recieved
    event OwnershipTransferRequested(address newPendingOwner);

    /// @notice Event emitted when an ownership transfer request is accepted by the pending owner
    event OwnershipTransferred(address newOwner, address oldOwner);

    /// @notice Initializes the contract owner
    constructor () public {
        owner = msg.sender;
        emit OwnershipTransferred(owner, address(0));
    }

    /// @notice Throws if called by any account other than the owner
    modifier onlyOwner() {
        require(msg.sender == owner, "OE01");
        _;
    }

    /// @notice Transfers ownership of the contract to a new owner
    /// @dev Can only be called by the current owner
    /// @param newOwner Address of new owner
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "OE02");
        pendingOwner = newOwner;

        emit OwnershipTransferRequested(newOwner);
    }

    /// @notice Pending owner accepts ownership of the contract
    /// @dev Only Pending owner can call this function
    function acceptTransferOwnership () external {
        require (pendingOwner != address(0), "OE03");
        require (pendingOwner == msg.sender, "OE04");
        
        emit OwnershipTransferred(pendingOwner, owner);
        owner = pendingOwner;
        pendingOwner = address(0);
    }
}
// File: HoldefiPrices.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.12;

import "./AggregatorV3Interface.sol";
import "./SafeMath.sol";
import "./HoldefiOwnable.sol";

interface ERC20DecimalInterface {
    function decimals () external view returns(uint256 res);
}
/// @title HoldefiPrices contract
/// @author Holdefi Team
/// @notice This contract is for getting tokens price
/// @dev This contract uses Chainlink Oracle to get the tokens price
/// @dev The address of ETH asset considered as 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
/// @dev Error codes description: 
///     E01: Asset should not be ETH
contract HoldefiPrices is HoldefiOwnable {

    using SafeMath for uint256;

    address constant private ethAddress = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    uint256 constant private valueDecimals = 30;

    struct Asset {
        uint256 decimals;
        AggregatorV3Interface priceContract;
    }
   
    mapping(address => Asset) public assets;

    /// @notice Event emitted when a new price aggregator is set for an asset
    event NewPriceAggregator(address indexed asset, uint256 decimals, address priceAggregator);

	/// @notice Initializes ETH decimals
    constructor() public {
        assets[ethAddress].decimals = 18;
    }

    /// @notice You cannot send ETH to this contract
    receive() payable external {
        revert();
    }

    /// @notice Gets price of selected asset from Chainlink
	/// @dev The ETH price is assumed to be 1
	/// @param asset Address of the given asset
    /// @return price Price of the given asset
    /// @return priceDecimals Decimals of the given asset
    function getPrice(address asset) public view returns (uint256 price, uint256 priceDecimals) {
        if (asset == ethAddress){
            price = 1;
            priceDecimals = 0;
        }
        else {
            (,int aggregatorPrice,,,) = assets[asset].priceContract.latestRoundData();
            priceDecimals = assets[asset].priceContract.decimals();
            if (aggregatorPrice > 0) {
                price = uint256(aggregatorPrice);
            }
            else {
                revert();
            }
        }
    }

    /// @notice Sets price aggregator for the given asset 
	/// @param asset Address of the given asset
    /// @param decimals Decimals of the given asset
    /// @param priceContractAddress Address of asset's price aggregator
    function setPriceAggregator(address asset, uint256 decimals, AggregatorV3Interface priceContractAddress)
        external
        onlyOwner
    { 
        require (asset != ethAddress, "E01");
        assets[asset].priceContract = priceContractAddress;

        try ERC20DecimalInterface(asset).decimals() returns (uint256 tokenDecimals) {
            assets[asset].decimals = tokenDecimals;
        }
        catch {
            assets[asset].decimals = decimals;
        }
        emit NewPriceAggregator(asset, assets[asset].decimals, address(priceContractAddress));
    }

    /// @notice Calculates the given asset value based on the given amount 
	/// @param asset Address of the given asset
    /// @param amount Amount of the given asset
    /// @return res Value calculated for asset based on the price and given amount
    function getAssetValueFromAmount(address asset, uint256 amount) external view returns (uint256 res) {
        uint256 decimalsDiff;
        uint256 decimalsScale;

        (uint256 price, uint256 priceDecimals) = getPrice(asset);
        uint256 calValueDecimals = priceDecimals.add(assets[asset].decimals);
        if (valueDecimals > calValueDecimals){
            decimalsDiff = valueDecimals.sub(calValueDecimals);
            decimalsScale =  10 ** decimalsDiff;
            res = amount.mul(price).mul(decimalsScale);
        }
        else {
            decimalsDiff = calValueDecimals.sub(valueDecimals);
            decimalsScale =  10 ** decimalsDiff;
            res = amount.mul(price).div(decimalsScale);
        }   
    }

    /// @notice Calculates the given amount based on the given asset value
    /// @param asset Address of the given asset
    /// @param value Value of the given asset
    /// @return res Amount calculated for asset based on the price and given value
    function getAssetAmountFromValue(address asset, uint256 value) external view returns (uint256 res) {
        uint256 decimalsDiff;
        uint256 decimalsScale;

        (uint256 price, uint256 priceDecimals) = getPrice(asset);
        uint256 calValueDecimals = priceDecimals.add(assets[asset].decimals);
        if (valueDecimals > calValueDecimals){
            decimalsDiff = valueDecimals.sub(calValueDecimals);
            decimalsScale =  10 ** decimalsDiff;
            res = value.div(decimalsScale).div(price);
        }
        else {
            decimalsDiff = calValueDecimals.sub(valueDecimals);
            decimalsScale =  10 ** decimalsDiff;
            res = value.mul(decimalsScale).div(price);
        }   
    }
}
// File: SafeMath.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.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 SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @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");
        return a - b;
    }

    /**
     * @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) {
        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, reverting 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) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting 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;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}