ETH Price: $3,568.31 (+8.22%)

Contract Diff Checker

Contract Name:
HegicERCPool

Contract Source Code:

File 1 of 1 : HegicERCPool

pragma solidity ^0.6.6;

library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
  }
library SafeMath {
     /**
      * @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) {
         return sub(a, b, "SafeMath: subtraction overflow");
     }

     /**
      * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
      * overflow (when the result is negative).
      *
      * 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);
         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-contracts/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) {
         return div(a, b, "SafeMath: division by zero");
     }

     /**
      * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
         // Solidity only automatically asserts when dividing by 0
         require(b > 0, errorMessage);
         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) {
         return mod(a, b, "SafeMath: modulo by zero");
     }

     /**
      * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
      * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
         require(b != 0, errorMessage);
         return a % b;
     }
 }
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
  }
interface AggregatorInterface {
  function latestAnswer() external view returns (int256);
  function latestTimestamp() external view returns (uint256);
  function latestRound() external view returns (uint256);
  function getAnswer(uint256 roundId) external view returns (int256);
  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
interface IUniswapFactory {
    function getExchange(IERC20 token)  external view returns (UniswapExchangeInterface exchange);
}
interface UniswapExchangeInterface {
    // // Address of ERC20 token sold on this exchange
    // function tokenAddress() external view returns (address token);
    // // Address of Uniswap Factory
    // function factoryAddress() external view returns (address factory);
    // // Provide Liquidity
    // function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
    // function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
    // // Get Prices
    function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
    // function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
    function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
    // function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
    // // Trade ETH to ERC20
    // function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256  tokens_bought);
    function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256  tokens_bought);
    // function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256  eth_sold);
    // function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256  eth_sold);
    // // Trade ERC20 to ETH
    // function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256  eth_bought);
    function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256  eth_bought);
    // function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256  tokens_sold);
    // function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256  tokens_sold);
    // // Trade ERC20 to ERC20
    // function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256  tokens_bought);
    // function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_bought);
    // function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256  tokens_sold);
    // function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_sold);
    // // Trade ERC20 to Custom Pool
    // function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256  tokens_bought);
    // function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_bought);
    // function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256  tokens_sold);
    // function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_sold);
    // // ERC20 comaptibility for liquidity tokens
    // bytes32 public name;
    // bytes32 public symbol;
    // uint256 public decimals;
    // function transfer(address _to, uint256 _value) external returns (bool);
    // function transferFrom(address _from, address _to, uint256 value) external returns (bool);
    // function approve(address _spender, uint256 _value) external returns (bool);
    // function allowance(address _owner, address _spender) external view returns (uint256);
    // function balanceOf(address _owner) external view returns (uint256);
    // function totalSupply() external view returns (uint256);
    // // Never use
    // function setup(address token_addr) external;
}
interface ILiquidityPool {
    event Withdraw(address indexed account, uint amount, uint writeAmount);
    event Provide (address indexed account, uint amount, uint writeAmount);
    function totalBalance() external view returns (uint amount);
    function lock(uint amount) external;
    function unlock(uint amount) external;
    function send(address payable account, uint amount) external;
}
interface IERCLiquidityPool is ILiquidityPool {
    function token() external view returns(IERC20);
}
interface ERC20Incorrect { // for the future
  function balanceOf(address who) external view returns (uint);
  function transfer(address to, uint value) external;
  function allowance(address owner, address spender) external view returns (uint);
  function transferFrom(address from, address to, uint value) external;
  function approve(address spender, uint value) external;

  event Approval(address indexed owner, address indexed spender, uint value);
  event Transfer(address indexed from, address indexed to, uint value);
}
interface SpreadLock {
  function highSpreadLockEnabled() external returns (bool);
}
contract Context {
  // Empty internal constructor, to prevent people from mistakenly deploying
  // an instance of this contract, which should be used via inheritance.
  constructor () internal { }
  
  function _msgSender() internal view virtual returns (address payable) {
    return msg.sender;
  }
  
  function _msgData() internal view virtual returns (bytes memory) {
    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    return msg.data;
  }
}
contract ERC20 is Context, IERC20 {
  using SafeMath for uint256;
  using Address for address;
  
  mapping (address => uint256) private _balances;
  
  mapping (address => mapping (address => uint256)) private _allowances;
  
  uint256 private _totalSupply;
  
  string private _name;
  string private _symbol;
  uint8 private _decimals;
  
  /**
  * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
  * a default value of 18.
  *
  * To select a different value for {decimals}, use {_setupDecimals}.
  *
  * All three of these values are immutable: they can only be set once during
  * construction.
  */
  constructor (string memory name, string memory symbol) public {
    _name = name;
    _symbol = symbol;
    _decimals = 18;
  }
  
  /**
  * @dev Returns the name of the token.
  */
  function name() public view returns (string memory) {
    return _name;
  }
  
  /**
  * @dev Returns the symbol of the token, usually a shorter version of the
  * name.
  */
  function symbol() public view returns (string memory) {
    return _symbol;
  }
  
  /**
  * @dev Returns the number of decimals used to get its user representation.
  * For example, if `decimals` equals `2`, a balance of `505` tokens should
  * be displayed to a user as `5,05` (`505 / 10 ** 2`).
  *
  * Tokens usually opt for a value of 18, imitating the relationship between
  * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
  * called.
  *
  * NOTE: This information is only used for _display_ purposes: it in
  * no way affects any of the arithmetic of the contract, including
  * {IERC20-balanceOf} and {IERC20-transfer}.
  */
  function decimals() public view returns (uint8) {
    return _decimals;
  }
  
  /**
  * @dev See {IERC20-totalSupply}.
  */
  function totalSupply() public view override returns (uint256) {
    return _totalSupply;
  }
  
  /**
  * @dev See {IERC20-balanceOf}.
  */
  function balanceOf(address account) public view override returns (uint256) {
    return _balances[account];
  }
  
  /**
  * @dev See {IERC20-transfer}.
  *
  * Requirements:
  *
  * - `recipient` cannot be the zero address.
  * - the caller must have a balance of at least `amount`.
  */
  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
    _transfer(_msgSender(), recipient, amount);
    return true;
  }
  
  /**
  * @dev See {IERC20-allowance}.
  */
  function allowance(address owner, address spender) public view virtual override returns (uint256) {
    return _allowances[owner][spender];
  }
  
  /**
  * @dev See {IERC20-approve}.
  *
  * Requirements:
  *
  * - `spender` cannot be the zero address.
  */
  function approve(address spender, uint256 amount) public virtual override returns (bool) {
    _approve(_msgSender(), spender, amount);
    return true;
  }
  
  /**
  * @dev See {IERC20-transferFrom}.
  *
  * Emits an {Approval} event indicating the updated allowance. This is not
  * required by the EIP. See the note at the beginning of {ERC20};
  *
  * Requirements:
  * - `sender` and `recipient` cannot be the zero address.
  * - `sender` must have a balance of at least `amount`.
  * - the caller must have allowance for ``sender``'s tokens of at least
  * `amount`.
  */
  function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
    _transfer(sender, recipient, amount);
    _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
    return true;
  }
  
  /**
  * @dev Atomically increases the allowance granted to `spender` by the caller.
  *
  * This is an alternative to {approve} that can be used as a mitigation for
  * problems described in {IERC20-approve}.
  *
  * Emits an {Approval} event indicating the updated allowance.
  *
  * Requirements:
  *
  * - `spender` cannot be the zero address.
  */
  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
    return true;
  }
  
  /**
  * @dev Atomically decreases the allowance granted to `spender` by the caller.
  *
  * This is an alternative to {approve} that can be used as a mitigation for
  * problems described in {IERC20-approve}.
  *
  * Emits an {Approval} event indicating the updated allowance.
  *
  * Requirements:
  *
  * - `spender` cannot be the zero address.
  * - `spender` must have allowance for the caller of at least
  * `subtractedValue`.
  */
  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
    return true;
  }
  
  /**
  * @dev Moves tokens `amount` from `sender` to `recipient`.
  *
  * This is internal function is equivalent to {transfer}, and can be used to
  * e.g. implement automatic token fees, slashing mechanisms, etc.
  *
  * Emits a {Transfer} event.
  *
  * Requirements:
  *
  * - `sender` cannot be the zero address.
  * - `recipient` cannot be the zero address.
  * - `sender` must have a balance of at least `amount`.
  */
  function _transfer(address sender, address recipient, uint256 amount) internal virtual {
    require(sender != address(0), "ERC20: transfer from the zero address");
    require(recipient != address(0), "ERC20: transfer to the zero address");
    
    _beforeTokenTransfer(sender, recipient, amount);
    
    _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
    _balances[recipient] = _balances[recipient].add(amount);
    emit Transfer(sender, recipient, amount);
  }
  
  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
  * the total supply.
  *
  * Emits a {Transfer} event with `from` set to the zero address.
  *
  * Requirements
  *
  * - `to` cannot be the zero address.
  */
  function _mint(address account, uint256 amount) internal virtual {
    require(account != address(0), "ERC20: mint to the zero address");
    
    _beforeTokenTransfer(address(0), account, amount);
    
    _totalSupply = _totalSupply.add(amount);
    _balances[account] = _balances[account].add(amount);
    emit Transfer(address(0), account, amount);
  }
  
  /**
  * @dev Destroys `amount` tokens from `account`, reducing the
  * total supply.
  *
  * Emits a {Transfer} event with `to` set to the zero address.
  *
  * Requirements
  *
  * - `account` cannot be the zero address.
  * - `account` must have at least `amount` tokens.
  */
  function _burn(address account, uint256 amount) internal virtual {
    require(account != address(0), "ERC20: burn from the zero address");
    
    _beforeTokenTransfer(account, address(0), amount);
    
    _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
    _totalSupply = _totalSupply.sub(amount);
    emit Transfer(account, address(0), amount);
  }
  
  /**
  * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
  *
  * This is internal function is equivalent to `approve`, and can be used to
  * e.g. set automatic allowances for certain subsystems, etc.
  *
  * Emits an {Approval} event.
  *
  * Requirements:
  *
  * - `owner` cannot be the zero address.
  * - `spender` cannot be the zero address.
  */
  function _approve(address owner, address spender, uint256 amount) internal virtual {
    require(owner != address(0), "ERC20: approve from the zero address");
    require(spender != address(0), "ERC20: approve to the zero address");
    
    _allowances[owner][spender] = amount;
    emit Approval(owner, spender, amount);
  }
  
  /**
  * @dev Sets {decimals} to a value other than the default one of 18.
  *
  * WARNING: This function should only be called from the constructor. Most
  * applications that interact with token contracts will not expect
  * {decimals} to ever change, and may work incorrectly if it does.
  */
  function _setupDecimals(uint8 decimals_) internal {
    _decimals = decimals_;
  }
  
  /**
  * @dev Hook that is called before any transfer of tokens. This includes
  * minting and burning.
  *
  * Calling conditions:
  *
  * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
  * will be to transferred to `to`.
  * - when `from` is zero, `amount` tokens will be minted for `to`.
  * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
  * - `from` and `to` are never both zero.
  *
  * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
  */
  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
contract Ownable is Context {
    address private _owner;

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

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

    /**
     * @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(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

contract HegicETHPool is ILiquidityPool, Ownable, ERC20("Hegic ETH LP Token", "writeETH"){
    using SafeMath for uint256;
    uint public lockedAmount;
    mapping(address => uint) private lastProvideBlock;

    /*
     * @nonce Send premiums to the liquidity pool
     **/
    receive() external payable {}

    /*
     * @nonce Returns the available amount in ETH for withdrawals
     * @return balance Unlocked amount
     */
    function availableBalance() public view returns (uint balance) {
        balance = totalBalance().sub(lockedAmount);
    }

    /*
     * @nonce Returns the ETH total balance provided to the pool
     * @return balance Pool balance
     */
    function totalBalance() public override view returns (uint balance) {
        balance = address(this).balance;
    }

    /*
     * @nonce A provider supplies ETH to the pool and receives writeETH tokens
     * @param minMint Low limit tokens that should be received
     * @return mint Received tokens amount
     */
    function provide(uint minMint) public payable returns (uint mint) {
        mint = provide();
        require(mint >= minMint, "Pool: Mint limit is too large");
    }

    /*
     * @nonce A provider supplies ETH to the pool and receives writeETH tokens
     * @return mint Tokens amount received
     */
    function provide() public payable returns (uint mint) {
        lastProvideBlock[msg.sender] = block.number;
        require(!SpreadLock(owner()).highSpreadLockEnabled(), "Pool: Locked");
        if(totalSupply().mul(totalBalance()) == 0)
            mint = msg.value.mul(1000);
        else
            mint = msg.value.mul(totalSupply()).div(totalBalance().sub(msg.value));
        require(mint > 0, "Pool: Amount is too small");
        emit Provide(msg.sender, msg.value, mint);
        _mint(msg.sender, mint);
    }

    /*
     * @nonce Provider burns writeETH and receives ETH back from the pool
     * @param amount ETH amount to receive
     * @param maxBurn Upper limit tokens that can be burned
     * @return burn Tokens amount burnt
     */
    function withdraw(uint amount, uint maxBurn) public returns (uint burn) {
        burn = withdraw(amount);
        require(burn <= maxBurn, "Pool: Burn limit is too small");
    }

    /*
     * @nonce Provider burns writeETH and receives ETH back from the pool
     * @param amount ETH amount to receive
     * @return burn Tokens amount burnt
     */
    function withdraw(uint amount) public returns (uint burn) {
        require(
            lastProvideBlock[msg.sender] != block.number,
            "Pool: Provide & Withdraw in one block"
        );
        require(amount <= availableBalance(), "Pool: Insufficient unlocked funds");
        burn = amount.mul(totalSupply()).div(totalBalance());
        require(burn <= balanceOf(msg.sender), "Pool: Amount is too large");
        require(burn > 0, "Pool: Amount is too small");
        _burn(msg.sender, burn);
        emit Withdraw(msg.sender, amount, burn);
        msg.sender.transfer(amount);
    }

    /*
     * @nonce Returns a share of the privider in ETH
     * @param account User address
     * @return A share of the provider in ETH
     */
    function shareOf(address account) public view returns (uint share){
        if(totalBalance() > 0) share = totalBalance()
            .mul(balanceOf(account))
            .div(totalSupply());
    }

    /*
     * @nonce calls by HegicCallOptions to lock funds
     * @param amount Funds that should be locked
     */
    function lock(uint amount) public override onlyOwner {
        require(
            lockedAmount.add(amount).mul(10).div(totalBalance()) < 8,
            "Pool: Insufficient unlocked funds" );
        lockedAmount = lockedAmount.add(amount);
    }

    /*
     * @nonce calls by HegicCallOptions to unlock funds
     * @param amount Funds that should be unlocked
     */
    function unlock(uint amount) public override onlyOwner {
        require(lockedAmount >= amount, "Pool: Insufficient locked funds");
        lockedAmount = lockedAmount.sub(amount);
    }

    /*
     * @nonce calls by HegicCallOptions to send funds to the provider after an option is closed
     * @param to Provider
     * @param amount Funds that should be sent
     */
    function send(address payable to, uint amount) public override onlyOwner {
        require(lockedAmount >= amount, "Pool: Insufficient locked funds");
        lockedAmount -= amount;
        to.transfer(amount);
    }
  }
contract HegicERCPool is IERCLiquidityPool, Ownable, ERC20("Hegic DAI LP Token", "writeDAI"){
      using SafeMath for uint256;
      uint public lockedAmount;
      mapping(address => uint) private lastProvideBlock;
      IERC20 public override token;

      /*
       * @return _token DAI Address
       */
      constructor(IERC20 _token) public {
          token = _token;
      }

      /*
       * @nonce Returns the available amount in DAI for withdrawals
       * @return balance Unlocked amount
       */
      function availableBalance() public view returns (uint balance) {
          balance = totalBalance().sub(lockedAmount);
      }

      /*
       * @nonce Returns the DAI total balance provided to the pool
       * @return balance Pool balance
       */
      function totalBalance() public override view returns (uint balance) {
           balance = token.balanceOf(address(this));
      }

      /*
       * @nonce A provider supplies DAI to the pool and receives writeDAI tokens
       * @param amount Amount provided
       * @param minMint Low limit tokens that should be received
       * @return mint Received tokens amount
       */
      function provide(uint amount, uint minMint) public returns (uint mint) {
          mint = provide(amount);
          require(mint >= minMint, "Pool: Mint limit is too large");
      }

      /*
       * @nonce A provider supplies DAI to the pool and receives writeDAI tokens
       * @param amount Provided tokens
       * @return mint Tokens amount received
       */
      function provide(uint amount) public returns (uint mint) {
          lastProvideBlock[msg.sender] = block.number;
          require(!SpreadLock(owner()).highSpreadLockEnabled(), "Pool: Locked");
          if(totalSupply().mul(totalBalance()) == 0)
              mint = amount.mul(1000);
          else
              mint = amount.mul(totalSupply()).div(totalBalance());

          require(mint > 0, "Pool: Amount is too small");
          emit Provide(msg.sender, amount, mint);
          require(
              token.transferFrom(msg.sender, address(this), amount),
              "Insufficient funds"
          );
          _mint(msg.sender, mint);
      }

      /*
       * @nonce Provider burns writeDAI and receives DAI back from the pool
       * @param amount DAI amount to receive
       * @param maxBurn Upper limit tokens that can be burned
       * @return burn Tokens amount burnt
       */
      function withdraw(uint amount, uint maxBurn) public returns (uint burn) {
          burn = withdraw(amount);
          require(burn <= maxBurn, "Pool: Burn limit is too small");
      }

      /*
       * @nonce Provider burns writeDAI and receives DAI back from the pool
       * @param amount DAI amount to receive
       * @return mint Tokens amount burnt
       */
      function withdraw(uint amount) public returns (uint burn) {
          require(
              lastProvideBlock[msg.sender] != block.number,
              "Pool: Provide & Withdraw in one block"
          );
          require(amount <= availableBalance(), "Pool: Insufficient unlocked funds");
          burn = amount.mul(totalSupply()).div(totalBalance());
          require(burn <= balanceOf(msg.sender), "Pool: Amount is too large");
          require(burn > 0, "Pool: Amount is too small");
          _burn(msg.sender, burn);
          emit Withdraw(msg.sender, amount, burn);
          require(
              token.transfer(msg.sender, amount),
              "Insufficient funds"
          );
      }

      /*
       * @nonce Returns a share of the provider in DAI
       * @param account User address
       * @return A share of the provider in DAI
       */
      function shareOf(address user) public view returns (uint share){
          if(totalBalance() > 0)
              share = totalBalance().mul(balanceOf(user)).div(totalSupply());
      }

      /*
       * @nonce calls by HegicPutOptions to lock funds
       * @param amount Funds that should be locked
       */
      function lock(uint amount) public override onlyOwner {
          require(
              lockedAmount.add(amount).mul(10).div( totalBalance() ) < 8,
              "Pool: Insufficient unlocked funds"
          );
          lockedAmount = lockedAmount.add(amount);
      }

      /*
       * @nonce calls by HegicPutOptions to unlock funds
       * @param amount Funds that should be unlocked
       */
      function unlock(uint amount) public override onlyOwner {
          require(lockedAmount >= amount, "Pool: Insufficient locked funds");
          lockedAmount = lockedAmount.sub(amount);
      }

      /*
       * @nonce calls by HegicPutOptions to send funds to the provider after an option is closed
       * @param to Provider
       * @param amount Funds that should be sent
       */
      function send(address payable to, uint amount) public override onlyOwner {
          require(lockedAmount >= amount, "Pool: Insufficient locked funds");
          lockedAmount -= amount;
          require(
              token.transfer(to, amount),
              "Insufficient funds"
          );
      }
  }
abstract contract HegicOptions is Ownable, SpreadLock {
      using SafeMath for uint;

      Option[] public options;
      uint public impliedVolRate = 18000;
      uint public maxSpread = 95;//%
      uint constant priceDecimals = 1e8;
      uint constant activationTime = 15 minutes;
      AggregatorInterface public priceProvider;
      IUniswapFactory public exchanges;
      IERC20 token;
      ILiquidityPool public pool;
      OptionType private optionType;
      bool public override highSpreadLockEnabled;

      event Create (uint indexed id, address indexed account, uint settlementFee, uint totalFee);
      event Exercise (uint indexed id, uint exchangeAmount);
      event Expire (uint indexed id);
      enum State { Active, Exercised, Expired }
      enum OptionType { Put, Call }
      struct Option {
          State state;
          address payable holder;
          uint strikeAmount;
          uint amount;
          uint expiration;
          uint activation;
      }

      /**
       * @param DAI The address of the DAI token
       * @param pp The address of the ChainLink ETH/USD price feed contract
       * @param ex The address of the Uniswap Factory
       * @param _type Put or call contract type
       */
      constructor(IERC20 DAI, AggregatorInterface pp, IUniswapFactory ex, OptionType _type) public {
          token = DAI;
          priceProvider = pp;
          exchanges = ex;
          optionType = _type;
      }

      /**
       * @notice Used to adjust prices
       * @param value New IVRate value
       */
      function setImpliedVolRate(uint value) public onlyOwner {
          require(value >= 10000, "ImpliedVolRate limit is too small");
          impliedVolRate = value;
      }

      /**
       * @notice Used to adjust the spread limit
       * @param value New maxSpread value
       */
      function setMaxSpread(uint value) public onlyOwner {
          require(value <= 95, "Spread limit is too large");
          maxSpread = value;
      }

      /**
       * @notice Used to get actual option's prices
       * @param period Option period in seconds (1 days <= period <= 8 weeks)
       * @param amount Option amount
       * @param strike Strike price of the option
       * @return total Total price needs to be paid
       * @return settlementFee Amount to be distributed between the HEGIC token holders
       * @return strikeFee Amount that covers the price difference in the ITM options
       * @return slippageFee Compensates the slippage during the exercising process
       * @return periodFee Option period fee
       */
      function fees(
          uint period,
          uint amount,
          uint strike
      )
          public
          view
          returns (
              uint total,
              uint settlementFee,
              uint strikeFee,
              uint slippageFee,
              uint periodFee
          )
      {
          uint currentPrice = uint(priceProvider.latestAnswer());
          settlementFee = getSettlementFee(amount);
          periodFee = getPeriodFee(amount, period, strike, currentPrice);
          slippageFee = getSlippageFee(amount);
          strikeFee = getStrikeFee(amount, strike, currentPrice);
          total = periodFee.add(slippageFee).add(strikeFee);
      }
      /**
       * @notice Creates ATM option
       * @param period Option period in seconds (1 days <= period <= 8 weeks)
       * @param amount Option amount
       * @return optionID Created option's ID
       */
      function createATM(uint period, uint amount) public payable returns (uint optionID) {
          return create(period, amount, uint(priceProvider.latestAnswer()));
      }

      /**
       * @notice Creates a new option
       * @param period Option period in sconds (1 days <= period <= 8 weeks)
       * @param amount Option amount
       * @param strike Strike price of an option
       * @return optionID Created option's ID
       */
      function create(uint period, uint amount, uint strike) public payable returns (uint optionID) {
          (uint total, uint settlementFee,,,) = fees(period, amount, strike);
          uint strikeAmount = strike.mul(amount) / priceDecimals;

          require(strikeAmount > 0,"Amount is too small");
          require(settlementFee < total,  "Premium is too small");
          require(period >= 1 days,"Period is too short");
          require(period <= 8 weeks,"Period is too long");
          require(msg.value == total, "Wrong value");
          payable( owner() ).transfer(settlementFee);

          optionID = options.length;
          options.push(
              Option(
                  State.Active,
                  msg.sender,
                  strikeAmount,
                  amount,
                  now + period,
                  now + activationTime
              )
          );

          sendPremium(total.sub(settlementFee));
          lockFunds(options[optionID]);
          emit Create(optionID, msg.sender, settlementFee, total);
      }

      /**
       * @notice Exercise your active option
       * @param optionID ID of your option
       */
      function exercise(uint optionID) public payable {
          Option storage option = options[optionID];

          require(option.expiration >= now, 'Option has expired');
          require(option.activation <= now, 'Option has not been activated yet');
          require(option.holder == msg.sender, "Wrong msg.sender");
          require(option.state == State.Active, "Wrong state");

          option.state = State.Exercised;
          swapFunds(option);

          uint amount = exchange();
          emit Exercise(optionID, amount);
      }

      /**
       * @notice Unlock array of options
       * @param optionIDs array of options
       */
      function unlockAll(uint[] memory optionIDs) public {
          for(uint i; i < optionIDs.length; unlock(optionIDs[i++])){}
      }

      /**
       * @notice Unlock funds locked in the expired options
       * @param optionID ID of the option
       */
      function unlock(uint optionID) public {
          Option storage option = options[optionID];
          require(option.expiration < now, "Option has not expired yet");
          require(option.state == State.Active, "Option is not active");
          option.state = State.Expired;
          unlockFunds(option);
          emit Expire(optionID);
      }

      /**
       * @notice Counts settlementFee
       * @param amount Option amount
       * @return fee Settlment fee amount
       */
      function getSettlementFee(uint amount) internal pure returns (uint fee) {
          fee = amount / 100;
      }

      /**
       * @notice Counts periodFee
       * @param amount Option amount
       * @param period Option period in seconds (1 days <= period <= 8 weeks)
       * @param strike Strike price of the option
       * @param currentPrice Current ETH price
       * @return fee Period fee amount
       */
      function getPeriodFee(
          uint amount,
          uint period,
          uint strike,
          uint currentPrice
      )
          internal
          view
          returns (uint fee)
      {
          if(optionType == OptionType.Put)
              fee = amount.mul(sqrt(period / 10)).mul(impliedVolRate)
                  .mul(strike).div(currentPrice).div(1e8);
          else
              fee = amount.mul(sqrt(period / 10)).mul(impliedVolRate)
                  .mul(currentPrice).div(strike).div(1e8);
      }

      /**
       * @notice Calculates slippageFee
       * @param amount Option amount
       * @return fee Slippage fee amount
       */
      function getSlippageFee(uint amount) internal pure returns (uint fee){
          if(amount > 10 ether) fee = amount.mul(amount) / 1e22;
      }

      /**
       * @notice Counts strikeFee
       * @param amount Option amount
       * @param strike Strike price of the option
       * @param currentPrice Current ether price
       * @return fee Strike fee amount
       */
      function getStrikeFee(
          uint amount,
          uint strike,
          uint currentPrice
      )
          internal
          view
          returns (uint fee)
      {
          if(strike > currentPrice && optionType == OptionType.Put)
              fee = (strike - currentPrice).mul(amount).div(currentPrice);
          if(strike < currentPrice && optionType == OptionType.Call)
              fee = (currentPrice - strike).mul(amount).div(currentPrice);
      }

      function exchange() public virtual returns (uint exchangedAmount);
      function sendPremium(uint amount) internal virtual;
      function lockFunds(Option memory option)  internal virtual;
      function swapFunds(Option memory option)  internal virtual;
      function unlockFunds(Option memory option) internal virtual;

      /**
       * @return res Square root of the number
       */
      function sqrt(uint x) private pure returns (uint res) {
          res = x;
          uint z = (x + 1) / 2;
          while (z < res) (res, z) = (z, (x / z + z) / 2);
      }
}
contract HegicCallOptions is HegicOptions {
    /**
     * @param DAI The address of the DAI token
     * @param priceProvider The address of the ChainLink ETH/USD price feed contract
     * @param uniswap The address of the Uniswap Factory
     */
    constructor(
        IERC20 DAI,
        AggregatorInterface priceProvider,
        IUniswapFactory uniswap
    )
        public
        HegicOptions(DAI, priceProvider, uniswap, HegicOptions.OptionType.Call)
    {
        pool = new HegicETHPool();
        approve();
    }

    /**
     * @notice Allows the Uniswap pool to swap the assets
     */
    function approve() public {
        token.approve(address(exchanges.getExchange(token)), uint(-1));
    }

    /**
     * @notice Swap a specific amount of DAI tokens for ETH and send it to the ETH liquidity pool
     * @return exchangedAmount An amount to receive from the Uniswap pool
     */
    function exchange() public override returns (uint exchangedAmount) {
        return exchange( token.balanceOf(address(this)) );
    }

    /**
     * @notice Swap a specific amount of DAI tokens for ETH and send it to the ETH liquidity pool
     * @param amount A specific amount to swap
     * @return exchangedAmount An amount that was received from the Uniswap pool
     */
    function exchange(uint amount) public returns (uint exchangedAmount) {
      UniswapExchangeInterface ex = exchanges.getExchange(token);
      uint exShare =  ex.getTokenToEthInputPrice(
          uint(priceProvider.latestAnswer()).mul(1e10)
      );
      if(exShare > maxSpread.mul(0.01 ether)){
          highSpreadLockEnabled = false;
          exchangedAmount = ex.tokenToEthTransferInput(
              amount,
              1,
              now + 1 minutes,
              address(pool)
          );
      } else {
          highSpreadLockEnabled = true;
      }
    }

    /**
     * @notice Distributes the premiums between the liquidity providers
     * @param amount Premiums amount that will be sent to the pool
     */
    function sendPremium(uint amount) override internal {
        payable(address(pool)).transfer(amount);
    }

    /**
     * @notice Locks the amount required for an option
     * @param option A specific option contract
     */
    function lockFunds(Option memory option) override internal {
        pool.lock(option.amount);
    }

    /**
     * @notice Receives DAI tokens from the user and sends ETH from the pool
     * @param option A specific option contract
     */
    function swapFunds(Option memory option) override internal {
        require(msg.value == 0, "Wrong msg.value");
        require(
            token.transferFrom(option.holder, address(this), option.strikeAmount),
            "Insufficient funds"
        );
        pool.send(option.holder, option.amount);
    }

    /**
     * @notice Locks the amount required for an option contract
     * @param option A specific option contract
     */
    function unlockFunds(Option memory option) override internal {
        pool.unlock(option.amount);
    }
}
contract HegicPutOptions is HegicOptions {
    /**
     * @param DAI The address of the DAI token
     * @param priceProvider The address of the ChainLink ETH/USD price feed contract
     * @param uniswap The address of the Uniswap Factory
     */
    constructor(
        IERC20 DAI,
        AggregatorInterface priceProvider,
        IUniswapFactory uniswap
    )
        public
        HegicOptions(DAI, priceProvider, uniswap, HegicOptions.OptionType.Put)
    {
        pool = new HegicERCPool(DAI);
    }

    /**
     * @notice Swaps ETH for DAI tokens and sends to the DAI liquidity pool
     * @return exchangedAmount Amount that is received from the Uniswap pool
     */
    function exchange() public override returns (uint) {
        return exchange(address(this).balance);
    }

    /**
     * @notice Swap a specific amount of ETH for DAI tokens and send it to the DAI liquidity pool
     * @param amount A specific amount to swap
     * @return exchangedAmount An amount to receive from the Uniswap pool
     */
    function exchange(uint amount) public returns (uint exchangedAmount) {
        UniswapExchangeInterface ex = exchanges.getExchange(token);
        uint exShare = ex.getEthToTokenInputPrice(1 ether);
        if(exShare > maxSpread.mul(uint(priceProvider.latestAnswer())).mul(1e8)) {
            highSpreadLockEnabled = false;
            exchangedAmount = ex.ethToTokenTransferInput {value: amount} (
                1,
                now + 1 minutes,
                address(pool)
            );
        } else {
            highSpreadLockEnabled = true;
        }
    }

    /**
     * @notice Distributes the premiums between the liquidity providers
     */
    function sendPremium(uint) override internal {
        exchange();
    }

    /**
     * @notice Locks the amount required for an option
     * @param option A specific option contract
     */
    function lockFunds(Option memory option) override internal {
        pool.lock(option.strikeAmount);
    }

    /**
     * @notice Receives ETH from the user and sends DAI tokens from the pool
     * @param option A specific option contract
     */
    function swapFunds(Option memory option) override internal {
        require(option.amount == msg.value, "Wrong msg.value");
        pool.send(option.holder, option.strikeAmount);
    }

    /**
     * @notice Locks the amount required for an option contract
     * @param option A specific option contract
     */
    function unlockFunds(Option memory option) override internal {
        pool.unlock(option.strikeAmount);
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):