// Contract: SktPublicSale
// Target function: function SKT2usd(uint256 usdAmount) private view returns (uint256)
/**
 *Submitted for verification at Etherscan.io on 2019-09-16
*/

/**
 *Submitted for verification at Etherscan.io on 2019-06-21
*/

pragma solidity ^0.5.7;

// Skt Public Sale

/**
 * @title SafeMath for uint256
 * @dev Unsigned math operations with safety checks that revert on error.
 */
library SafeMath256 {
    /**
     * @dev Adds two unsigned integers, reverts on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        assert(c >= a);
        return c;
    }

    /**
     * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    /**
     * @dev Multiplies two unsigned integers, reverts on overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        if (a == 0) {
            return 0;
        }
        c = a * b;
        assert(c / a == b);
        return c;
    }

    /**
     * @dev Integer division of two unsigned integers truncating the quotient,
     * reverts on division by zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b > 0);
        uint256 c = a / b;
        assert(a == b * c + a % b);
        return a / b;
    }

    /**
     * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
     * reverts when dividing by zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}

/**
 * @title SafeMath for uint16
 * @dev Unsigned math operations with safety checks that revert on error.
 */
library SafeMath16 {
    /**
     * @dev Adds two unsigned integers, reverts on overflow.
     */
    function add(uint16 a, uint16 b) internal pure returns (uint16 c) {
        c = a + b;
        assert(c >= a);
        return c;
    }

    /**
     * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint16 a, uint16 b) internal pure returns (uint16) {
        assert(b <= a);
        return a - b;
    }

    /**
     * @dev Multiplies two unsigned integers, reverts on overflow.
     */
    function mul(uint16 a, uint16 b) internal pure returns (uint16 c) {
        if (a == 0) {
            return 0;
        }
        c = a * b;
        assert(c / a == b);
        return c;
    }

    /**
     * @dev Integer division of two unsigned integers truncating the quotient,
     * reverts on division by zero.
     */
    function div(uint16 a, uint16 b) internal pure returns (uint16) {
        assert(b > 0);
        uint256 c = a / b;
        assert(a == b * c + a % b);
        return a / b;
    }

    /**
     * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
     * reverts when dividing by zero.
     */
    function mod(uint16 a, uint16 b) internal pure returns (uint16) {
        require(b != 0);
        return a % b;
    }
}


/**
 * @title Ownable
 */
contract Ownable {
    address private _owner;
    address payable internal _receiver;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event ReceiverChanged(address indexed previousReceiver, address indexed newReceiver);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract
     * to the sender account.
     */
    constructor () internal {
        _owner = msg.sender;
        _receiver = msg.sender;
    }

    /**
     * @return The address of the owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == _owner);
        _;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != _owner);
        address __previousOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(__previousOwner, newOwner);
    }

   
}


/**
 * @title Pausable
 * @dev Base contract which allows children to implement an emergency stop mechanism.
 */
contract Pausable is Ownable {
    bool private _paused;

    event Paused(address account);
    event Unpaused(address account);

    constructor () internal {
        _paused = false;
    }

    /**
     * @return Returns true if the contract is paused, false otherwise.
     */
    function paused() public view returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     */
    modifier whenNotPaused() {
        require(!_paused, "Paused.");
        _;
    }

    /**
     * @dev Called by a pauser to pause, triggers stopped state.
     */
    function setPaused(bool state) external onlyOwner {
        if (_paused && !state) {
            _paused = false;
            emit Unpaused(msg.sender);
        } else if (!_paused && state) {
            _paused = true;
            emit Paused(msg.sender);
        }
    }
}


/**
 * @title ERC20 interface
 * @dev see https://eips.ethereum.org/EIPS/eip-20
 */
interface IERC20 {
    function balanceOf(address owner) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
}


/**
 * @title Skt interface
 */
interface ISkt {
    function balanceOf(address owner) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function inWhitelist(address account) external view returns (bool);
    function referrer(address account) external view returns (address);
    function refCount(address account) external view returns (uint256);
}


/**
 * @title Skt Public Sale
 */
contract SktPublicSale is Ownable, Pausable{
    using SafeMath16 for uint16;
    using SafeMath256 for uint256;

    // Skt
    ISkt public SKT = ISkt(0x2fB74C37Fb2C8DC76beA1910737aa9E3e2b53535);

    // Start timestamp
    uint32 _startTimestamp;

    // Audit ether price
    uint256 private _etherPrice;    

    // Referral rewards, 35% for 15 levels
    uint16 private WHITELIST_REF_REWARDS_PCT_SUM = 35; 
	
    uint16[3] private WHITELIST_REF_REWARDS_PCT = [
        15,  // 15% for Level.1
        12,  // 12% for Level.2
        8   // 8% for Level.3
    ];
	
	
    // Wei & Gas
    uint72 private WEI_MIN = 0.1 ether;     
    uint72 private WEI_MAX = 100 ether;     
    uint72 private WEI_BONUS = 10 ether;    
    uint24 private GAS_MIN = 3000000;       
    uint24 private GAS_EX  = 1500000;        

    // Price
    uint256 private SKT_USD_PRICE_START = 100000;       
    uint256 private SKT_USD_PRICE_STEP = 10000;         
   
    uint256 private STAGE_USD_CAP_START = 9000000000000;    
    uint256 private STAGE_USD_CAP_STEP = 900000000000;       
    uint256 private STAGE_USD_CAP_MAX = 90000000000000;   
    
    uint256 private STAGE_SKT_CAP_START = 900000000000;    
    uint256 private STAGE_SKT_CAP_STEP  = 900000000000;       
    uint256 private STAGE_SKT_CAP_MAX   = 90000000000000;    
    
    uint256 private _SKTUsdPrice = SKT_USD_PRICE_START; 
    
    uint16 private STAGE_MAX = 100;  
    

    uint16 private _stage;
   

    // Sum
    uint256 private _txs; 
    uint256 private _SKTTxs;
    uint256 private _SKTBonusTxs;
    uint256 private _SKTWhitelistTxs;
    uint256 private _SKTIssued;
    uint256 private _SKTBonus;
    uint256 private _SKTWhitelist;
    uint256 private _usdSold;
    uint256 private _weiSold;  
    uint256 private _weiRefRewarded;  
    uint256 private _weiTeam; 
    


    // During tx
    bool private _inWhitelist_;
   
    uint16[] private _rewards_;
    address[] private _referrers_;

    

    // Stage
    mapping (uint16 => uint256) private _stageUsdSold; 
    mapping (uint16 => uint256) private _stageSKTIssued; 
   

   

    // Account
    mapping (address => uint256) private _accountSKTIssued;
    mapping (address => uint256) private _accountSKTBonus;
    mapping (address => uint256) private _accountSKTWhitelisted;
    mapping (address => uint256) private _accountWeiPurchased; 
    mapping (address => uint256) private _accountWeiRefRewarded; 

  
   

    // Events
    event AuditEtherPriceChanged(uint256 value, address indexed account);
    event AuditEtherPriceAuditorChanged(address indexed account, bool state);

    event SKTBonusTransfered(address indexed to, uint256 amount);
    event SKTWhitelistTransfered(address indexed to, uint256 amount);
    event SKTIssuedTransfered(uint16 stageIndex, address indexed to, uint256 SKTAmount, uint256 auditEtherPrice, uint256 weiUsed);

    event StageClosed(uint256 _stageNumber, address indexed account);
   
    event TeamWeiTransfered(address indexed to, uint256 amount);
    event PendingWeiTransfered(address indexed to, uint256 amount);


    /**
     * @dev Start timestamp.
     */
    function startTimestamp() public view returns (uint32) {
        return _startTimestamp;
    }

    /**
     * @dev Set start timestamp.
     */
    function setStartTimestamp(uint32 timestamp) external onlyOwner {
        _startTimestamp = timestamp;
    }

  

    /**
     * @dev Set audit ether price.
     */
    function setEtherPrice(uint256 value) external onlyOwner {
        _etherPrice = value;
        emit AuditEtherPriceChanged(value, msg.sender);
    }

 

   

    /**
     * @dev Stage SKT price in USD, by stage index.
     */
	//返回 当前 阶段 USD的 值
	function stageSKTUsdPrice(uint16 stageIndex) private view returns (uint256) {
		
        return SKT_USD_PRICE_START.add(SKT_USD_PRICE_STEP.mul(stageIndex));
    }

    /**
     * @dev wei => USD
     */
    function wei2usd(uint256 amount) private view returns (uint256) {
        return amount.mul(_etherPrice).div(1 ether);
    }

    /**
     * @dev USD => wei
     */
    function usd2wei(uint256 amount) private view returns (uint256) {
        return amount.mul(1 ether).div(_etherPrice);
    }

    /**
     * @dev USD => SKT
     */
    function usd2SKT(uint256 usdAmount) private view returns (uint256) {
      
		return usdAmount.mul(1000000).div(_SKTUsdPrice);
    }