// File: IERC20.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}

// File: SafeMath.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/**
 * @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, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) public 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) public 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) public 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) public 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) public 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) public 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) public 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) public pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

// File: Savix.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./SavixSupply.sol";

contract Savix is IERC20
{
    using SafeMath for uint256;

    address private _owner;
    string private constant NAME = "Savix";
    string private constant SYMBOL = "SVX";
    uint private constant DECIMALS = 9;
    uint private constant MINTIMEWIN = 60;
    uint private constant CONSTINTEREST = 8;
    uint private _constGradient = 0;

    bool private _stakingActive = false;
    uint256 private _stakingSince = 0;

    uint256 private constant MAX_UINT256 = 2**256 - 1;
    uint256 private constant MAX_UINT128 = 2**128 - 1;
    uint256 private constant INITIAL_TOKEN_SUPPLY = 10**5 * 10**DECIMALS;

    // TOTAL_FRAGMENTS is a multiple of INITIAL_TOKEN_SUPPLY so that _fragmentsPerToken is an integer.
    // Use the highest value that fits in a uint256 for max granularity.
    uint256 private constant TOTAL_FRAGMENTS = MAX_UINT256 - (MAX_UINT256 % INITIAL_TOKEN_SUPPLY);

    uint256 private constant MAX_SUPPLY = MAX_UINT128;  // (2^128) - 1
    
    uint256 private _totalSupply = INITIAL_TOKEN_SUPPLY;
    uint256 private _lastTotalSupply = INITIAL_TOKEN_SUPPLY;
    uint256 private _lastAdjustTime = 0;

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;
    uint256[2][] private _supplyMap;

    bool _transferLock = true;

    constructor() public
    {
        _owner = msg.sender;
        
         _totalSupply = INITIAL_TOKEN_SUPPLY;
        _balances[_owner] = TOTAL_FRAGMENTS;

        _supplyMap.push([0, 100000 * 10**DECIMALS]);
        _supplyMap.push([7 * SavixSupply.SECPERDAY, 115000 * 10**DECIMALS]);
        _supplyMap.push([30 * SavixSupply.SECPERDAY, 130000 * 10**DECIMALS]);
        _supplyMap.push([6 * 30 * SavixSupply.SECPERDAY, 160000 * 10**DECIMALS]);
        _supplyMap.push([12 * 30 * SavixSupply.SECPERDAY, 185000 * 10**DECIMALS]);
        _supplyMap.push([18 * 30 * SavixSupply.SECPERDAY, 215000 * 10**DECIMALS]);
        _supplyMap.push([24 * 30 * SavixSupply.SECPERDAY, 240000 * 10**DECIMALS]);
        _supplyMap.push([48 * 30 * SavixSupply.SECPERDAY, 300000 * 10**DECIMALS]);
        
        _constGradient = SafeMath.div(INITIAL_TOKEN_SUPPLY * CONSTINTEREST, 360 * SavixSupply.SECPERDAY * 100);
    }
    
    modifier validRecipient(address to)
    {
        require(to != address(0) && to != address(this));
        _;
    }
    
    modifier onlyOwner 
    {
        require(msg.sender == _owner,"Only owner can call this function.");
        _;
    }

    function isContract(address _addr) private view returns (bool)
    {
        uint32 size;
        assembly {
            size := extcodesize(_addr)
        }
        return (size > 0);
    }

    function supplyMap() external view returns (uint256[2][] memory) 
    {
        return _supplyMap;
    }

    function initialSupply() external pure returns (uint256) 
    {
        return INITIAL_TOKEN_SUPPLY;
    }

    function finalGradient() external view returns (uint) 
    {
        return _constGradient;
    }

    function lastAdjustTime() external view returns (uint) 
    {
        return _lastAdjustTime;
    }

    function lastTotalSupply() external view returns (uint) 
    {
        return _lastTotalSupply;
    }

    function unlockTransfers() 
      external 
      onlyOwner
    {
        _transferLock = false;
    }

    function startStaking() 
      external 
      onlyOwner
    {
        _stakingActive = true;
        _stakingSince = block.timestamp;

        _constGradient = SafeMath.div(INITIAL_TOKEN_SUPPLY * CONSTINTEREST, 360 * SavixSupply.SECPERDAY *100);
        _totalSupply = _supplyMap[0][1];
        _lastTotalSupply = _totalSupply;
        _lastAdjustTime = 0;

    }
    
    function calculateNewSupply(uint256 calcTime) public returns (uint256)
    {
        SavixSupply.AdjustedSupplyData memory supplyData = SavixSupply.getAdjustedSupply(_supplyMap, calcTime, _lastAdjustTime, _totalSupply, _constGradient);
        if (_totalSupply == supplyData.newSupply)
            return _totalSupply;

        _lastTotalSupply = _totalSupply;
        _totalSupply = supplyData.newSupply;
        _lastAdjustTime = supplyData.adjustTime;
        
        return supplyData.newSupply; 
    }


    function name() external pure returns (string memory) 
    {
        return NAME;
    }

    function symbol() external pure returns (string memory)
    {
        return SYMBOL;
    }

    function decimals() external pure returns (uint8)
    {
        return uint8(DECIMALS);
    }

    function stakingActive() external view returns (bool)
    {
        return _stakingActive;
    }

    function stakingSince() external view returns (uint256)
    {
        return _stakingSince;
    }

    function stakingFrequence() external pure returns (uint)
    {
        return SavixSupply.MINTIMEWIN;
    }

    
    function totalSupply() override external view returns (uint256)
    {
        return _totalSupply;
    }
    
    function dailyInterest() external view returns (uint)
    {
            return SavixSupply.getDailyInterest(block.timestamp - _stakingSince, _lastAdjustTime, _totalSupply, _lastTotalSupply); 
    }

    function balanceOf(address account) override public view returns (uint256)
    {
        return _balances[account].div(TOTAL_FRAGMENTS.div(_totalSupply));
    }

    function _calculateFragments(uint256 value) internal returns (uint256)
    {
        if(_stakingActive == true)
            _totalSupply = calculateNewSupply(block.timestamp - _stakingSince);

        return value.mul(TOTAL_FRAGMENTS.div(_totalSupply));
    }

    function transfer(address to, uint256 value) override
        external
        validRecipient(to)
        returns (bool)
    {
        require(msg.sender == _owner || _transferLock == false || isContract(msg.sender), "Tokens are locked for transfer");
        uint256 rAmount = _calculateFragments(value);
        _balances[msg.sender] = _balances[msg.sender].sub(rAmount,"ERC20: transfer amount exceeds balance");
        _balances[to] = _balances[to].add(rAmount);
        emit Transfer(msg.sender, to, value);       
        return true;
    }

    /**
     *
     * 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 value) 
        override
        external
        returns (bool)
    {
        require(msg.sender == _owner || _transferLock == false || isContract(sender), "Tokens are locked for transfer");
        uint256 rAmount = _calculateFragments(value);
        _balances[sender] = _balances[sender].sub(rAmount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(rAmount);
        emit Transfer(sender, recipient, value);
        _allowances[sender][msg.sender] = _allowances[sender][msg.sender].sub(value,"ERC20: transfer amount exceeds allowance");
        return true;
    }

    function allowance(address owner, address spender) override external view returns (uint256)
    {
        return _allowances[owner][spender];
    }

    
    function increaseAllowance(address spender, uint256 addedValue) external returns (bool)
    {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
        return true;
    }
    
    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool)
    {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue,"ERC20: decreased allowance below zero"));
        return true;
    }

    
    function approve(address spender, uint256 value) override external returns (bool) 
    {
        _allowances[msg.sender][spender] = 0;
        _approve(msg.sender, spender, value);
        return true;
    }

    function _approve(address owner, address spender, uint256 value) internal 
    {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        // In order to exclude front-running attacks:
        // To change the approve amount you first have to reduce the addresses`
        // allowance to zero by calling `approve(_spender, 0)` if it is not
        // already 0 to mitigate the race condition described here:
        // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((value == 0 || _allowances[msg.sender][spender] == 0), "possible front-running attack");
        
        _allowances[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    // use same logic to adjust balances as in function transfer
    // only distribute from owner wallet (ecosystem fund)
    // gas friendly way to do airdrops or giveaways
    function distributeTokens(address[] memory addresses, uint256 value)
        external
        onlyOwner
    {
        uint256 rAmount = _calculateFragments(value);
        _balances[_owner] = _balances[_owner].sub(rAmount * addresses.length,"ERC20: distribution total amount exceeds balance");
        for (uint i = 0; i < addresses.length; i++)
        {
            _balances[addresses[i]] = _balances[addresses[i]].add(rAmount);
            emit Transfer(_owner, addresses[i], value);       
        }
    }

    // use same logic to adjust balances as in function transfer
    // only distribute from owner wallet (ecosystem fund)
    // gas friendly way to do airdrops or giveaways
    function distributeTokensFlexSum(address[] memory addresses, uint256[] memory values)
        external
        onlyOwner
    {
        // there has to be exacly 1 value per address
        require(addresses.length == values.length); // Overflow check

        uint256 valuesum = 0;
        for (uint i = 0; i < values.length; i++)
            valuesum += values[i];

        _balances[_owner] = _balances[_owner].sub( _calculateFragments(valuesum),"ERC20: distribution total amount exceeds balance");

        for (uint i = 0; i < addresses.length; i++)
        {
            _balances[addresses[i]] = _balances[addresses[i]].add(_calculateFragments(values[i]));
            emit Transfer(_owner, addresses[i], values[i]);       
        }
    }
    
    function getOwner() 
      external
      view 
    returns(address)
    {
        return _owner;
    }
}

// File: SavixSupply.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./SafeMath.sol";

/**
 * @dev savix interest and supply calculations.
 *
*/
 library SavixSupply {
     
    uint256 public constant MAX_UINT256 = 2**256 - 1;
    uint256 public constant MAX_UINT128 = 2**128 - 1;
    uint public constant MINTIMEWIN = 60;
    uint public constant SECPERDAY = 3600 * 24;
    uint public constant DECIMALS = 9;

    struct SupplyWinBoundery 
    {
        uint256 x1;
        uint256 x2;
        uint256 y1;
        uint256 y2;
    }

    struct AdjustedSupplyData 
    {
        uint256 newSupply;
        uint256 adjustTime;
        uint adjustGradient;
    }
    
    function getSupplyWindow(uint256[2][] memory map, uint256 calcTime) internal pure returns (SupplyWinBoundery memory)
    {
        SupplyWinBoundery memory winBound;
        
        winBound.x1 = 0;
        winBound.x2 = 0;

        winBound.y1 = map[0][1];
        winBound.y2 = 0;

        for (uint i=0; i < map.length; i++) {
            if (map[i][0] == 0) continue;
            if (calcTime < map[i][0])
            {
                winBound.x2 = map[i][0];
                winBound.y2 = map[i][1];
                break;
            }
            else
            {
                winBound.x1 = map[i][0];
                winBound.y1 = map[i][1];
            }
        }
        if (winBound.x2 == 0) winBound.x2 = MAX_UINT256;
        if (winBound.y2 == 0) winBound.y2 = MAX_UINT128;
        return winBound;
    }


    // function to calculate new Supply with SafeMath for divisions only, shortest (cheapest) form
    function getAdjustedSupply(uint256[2][] memory map, uint256 transactionTime, uint256 lastAdjustTime, uint256 currentSupply, uint constGradient) internal pure returns (AdjustedSupplyData memory)
    {
        AdjustedSupplyData memory supplyData;
        supplyData.newSupply = currentSupply;
        supplyData.adjustTime = transactionTime;
        supplyData.adjustGradient = 0;
        
        // return unchanged supply if less than MINTIMEWIN secounds have passed
        // if (lastAdjustTime > 0 && transactionTime - lastAdjustTime < MINTIMEWIN)
        if (transactionTime - lastAdjustTime < MINTIMEWIN)
        {
            return (supplyData);
        }

        if (transactionTime >= map[map.length-1][0])
        {
            supplyData.newSupply = map[map.length-1][1] + constGradient * (transactionTime - map[map.length-1][0]);
            supplyData.adjustGradient = constGradient;
            
            return (supplyData);
        }
        
        SupplyWinBoundery memory winBound = getSupplyWindow(map, transactionTime);
        supplyData.adjustGradient = SafeMath.div(winBound.y2 - winBound.y1, winBound.x2 - winBound.x1);
        supplyData.newSupply = winBound.y1 + supplyData.adjustGradient * (transactionTime - winBound.x1);

        return (supplyData);
    }

    function getDailyInterest(uint256 currentTime, uint256 lastAdjustTime, uint256 currentSupply, uint256 lastSupply) internal pure returns (uint)
    {
            if (currentTime <= lastAdjustTime)
                return currentTime;
                
            uint256 InterestSinceLastAdjust = SafeMath.div((currentSupply - lastSupply) * 100 * 10**DECIMALS, lastSupply);
            return (SafeMath.div(InterestSinceLastAdjust * SECPERDAY, currentTime - lastAdjustTime));
    }
 }