contract ReconTokenFactory is ERC20Interface, Owned {
    using SafeMath for uint;

    string public constant name = "RECON";
    string public constant symbol = "RECON";
    uint8 public constant decimals = 18;

    uint constant public ReconToMicro = uint(1000000000000000000);

    // This constants reflects RECON token distribution

    uint constant public investorSupply                   =  25000000000 * ReconToMicro;
    uint constant public adviserSupply                    =     25000000 * ReconToMicro;
    uint constant public bountySupply                     =     25000000 * ReconToMicro;

    uint constant public _totalSupply                     = 100000000000 * ReconToMicro;
    uint constant public preICOSupply                     =   5000000000 * ReconToMicro;
    uint constant public presaleSupply                    =   5000000000 * ReconToMicro;
    uint constant public crowdsaleSupply                  =  10000000000 * ReconToMicro;
    uint constant public preICOprivate                    =     99000000 * ReconToMicro;

    uint constant public Reconowner                       =    101000000 * ReconToMicro;
    uint constant public ReconnewOwner                    =    100000000 * ReconToMicro;
    uint constant public Reconminter                      =     50000000 * ReconToMicro;
    uint constant public ReconfeeAccount                  =     50000000 * ReconToMicro;
    uint constant public Reconspender                     =     50000000 * ReconToMicro;
    uint constant public ReconrecoveredAddress            =     50000000 * ReconToMicro;
    uint constant public ProprityfromReconBank            =    200000000 * ReconToMicro;
    uint constant public ReconManager                     =    200000000 * ReconToMicro;

    uint constant public ReconCashinB2B                   =   5000000000 * ReconToMicro;
    uint constant public ReconSwitchC2C                   =   5000000000 * ReconToMicro;
    uint constant public ReconCashoutB2C                  =   5000000000 * ReconToMicro;
    uint constant public ReconInvestment                  =   2000000000 * ReconToMicro;
    uint constant public ReconMomentum                    =   2000000000 * ReconToMicro;
    uint constant public ReconReward                      =   2000000000 * ReconToMicro;
    uint constant public ReconDonate                      =   1000000000 * ReconToMicro;
    uint constant public ReconTokens                      =   4000000000 * ReconToMicro;
    uint constant public ReconCash                        =   4000000000 * ReconToMicro;
    uint constant public ReconGold                        =   4000000000 * ReconToMicro;
    uint constant public ReconCard                        =   4000000000 * ReconToMicro;
    uint constant public ReconHardriveWallet              =   2000000000 * ReconToMicro;
    uint constant public RecoinOption                     =   1000000000 * ReconToMicro;
    uint constant public ReconPromo                       =    100000000 * ReconToMicro;
    uint constant public Reconpatents                     =   1000000000 * ReconToMicro;
    uint constant public ReconSecurityandLegalFees        =   1000000000 * ReconToMicro;
    uint constant public PeerToPeerNetworkingService      =   1000000000 * ReconToMicro;
    uint constant public Reconia                          =   2000000000 * ReconToMicro;

    uint constant public ReconVaultXtraStock              =   7000000000 * ReconToMicro;
    uint constant public ReconVaultSecurityStock          =   5000000000 * ReconToMicro;
    uint constant public ReconVaultAdvancePaymentStock    =   5000000000 * ReconToMicro;
    uint constant public ReconVaultPrivatStock            =   4000000000 * ReconToMicro;
    uint constant public ReconVaultCurrencyInsurancestock =   4000000000 * ReconToMicro;
    uint constant public ReconVaultNextStock              =   4000000000 * ReconToMicro;
    uint constant public ReconVaultFuturStock             =   4000000000 * ReconToMicro;



    // This variables accumulate amount of sold RECON during
    // presale, crowdsale, or given to investors as bonus.
    uint public presaleSold = 0;
    uint public crowdsaleSold = 0;
    uint public investorGiven = 0;

    // Amount of ETH received during ICO
    uint public ethSold = 0;

    uint constant public softcapUSD = 20000000000;
    uint constant public preicoUSD  = 5000000000;

    // Presale lower bound in dollars.
    uint constant public crowdsaleMinUSD = ReconToMicro * 10 * 100 / 12;
    uint constant public bonusLevel0 = ReconToMicro * 10000 * 100 / 12; // 10000$
    uint constant public bonusLevel100 = ReconToMicro * 100000 * 100 / 12; // 100000$

    // The tokens made available to the public will be in 13 steps
    // for a maximum of 20% of the total supply (see doc for checkTransfer).
    // All dates are stored as timestamps.
    uint constant public unlockDate1  = 1541890800; // 11-11-2018 00:00:00  [1%]  Recon Manager
    uint constant public unlockDate2  = 1545346800; // 21-12-2018 00:00:00  [2%]  Recon Cash-in (B2B)
    uint constant public unlockDate3  = 1549062000; // 02-02-2019 00:00:00  [2%]  Recon Switch (C2C)
    uint constant public unlockDate4  = 1554328800; // 04-04-2019 00:00:00  [2%]  Recon Cash-out (B2C)
    uint constant public unlockDate5  = 1565215200; // 08-08-2019 00:00:00  [2%]  Recon Investment & Recon Momentum
    uint constant public unlockDate6  = 1570658400; // 10-10-2019 00:00:00  [2%]  Recon Reward
    uint constant public unlockDate7  = 1576105200; // 12-12-2019 00:00:00  [1%]  Recon Donate
    uint constant public unlockDate8  = 1580598000; // 02-02-2020 00:00:00  [1%]  Recon Token
    uint constant public unlockDate9  = 1585951200; // 04-04-2020 00:00:00  [2%]  Recon Cash
    uint constant public unlockDate10 = 1591394400; // 06-06-2020 00:00:00  [1%]  Recon Gold
    uint constant public unlockDate11 = 1596837600; // 08-08-2020 00:00:00  [2%]  Recon Card
    uint constant public unlockDate12 = 1602280800; // 10-10-2020 00:00:00  [1%]  Recon Hardrive Wallet
    uint constant public unlockDate13 = 1606863600; // 02-12-2020 00:00:00  [1%]  Recoin Option

    // The tokens made available to the teams will be made in 4 steps
    // for a maximum of 80% of the total supply (see doc for checkTransfer).
    uint constant public teamUnlock1 = 1544569200; // 12-12-2018 00:00:00  [25%]
    uint constant public teamUnlock2 = 1576105200; // 12-12-2019 00:00:00  [25%]
    uint constant public teamUnlock3 = 1594072800; // 07-07-2020 00:00:00  [25%]
    uint constant public teamUnlock4 = 1608505200; // 21-12-2020 00:00:00  [25%]

    uint constant public teamETHUnlock1 = 1544569200; // 12-12-2018 00:00:00
    uint constant public teamETHUnlock2 = 1576105200; // 12-12-2019 00:00:00
    uint constant public teamETHUnlock3 = 1594072800; // 07-07-2020 00:00:00

    //https://casperproject.atlassian.net/wiki/spaces/PROD/pages/277839878/Smart+contract+ICO
    // Presale 10.06.2018 - 22.07.2018
    // Crowd-sale 23.07.2018 - 2.08.2018 (16.08.2018)
    uint constant public presaleStartTime     = 1541890800; // 11-11-2018 00:00:00
    uint constant public crowdsaleStartTime   = 1545346800; // 21-12-2018 00:00:00
    uint          public crowdsaleEndTime     = 1609455599; // 31-12-2020 23:59:59
    uint constant public crowdsaleHardEndTime = 1609455599; // 31-12-2020 23:59:59
    //address constant ReconrWallet = 0x0A5f85C3d41892C934ae82BDbF17027A20717088;
    constructor() public {
        admin = owner;
        balances[owner] = _totalSupply;
        emit Transfer(address(0), owner, _totalSupply);
    }

    modifier onlyAdmin {
        require(msg.sender == admin);
        _;
    }

    modifier onlyOwnerAndDirector {
        require(msg.sender == owner || msg.sender == director);
        _;
    }

    address admin;
    function setAdmin(address _newAdmin) public onlyOwnerAndDirector {
        admin = _newAdmin;
    }

    address director;
    function setDirector(address _newDirector) public onlyOwner {
        director = _newDirector;
    }

    bool assignedPreico = false;
    // @notice assignPreicoTokens transfers 3x tokens to pre-ICO participants (99,000,000)
    function assignPreicoTokens() public onlyOwnerAndDirector {
        require(!assignedPreico);
        assignedPreico = true;

        _freezeTransfer(0x4Bdff2Cc40996C71a1F16b72490d1a8E7Dfb7E56, 3 * 1000000000000000000000000); // Account_34
        _freezeTransfer(0x9189AC4FA7AdBC587fF76DD43248520F8Cb897f3, 3 * 1000000000000000000000000); // Account_35
        _freezeTransfer(0xc1D3DAd07A0dB42a7d34453C7d09eFeA793784e7, 3 * 1000000000000000000000000); // Account_36
        _freezeTransfer(0xA0BC1BAAa5318E39BfB66F8Cd0496d6b09CaE6C1, 3 * 1000000000000000000000000); // Account_37
        _freezeTransfer(0x9a2912F145Ab0d5b4aE6917A8b8ddd222539F424, 3 * 1000000000000000000000000); // Account_38
        _freezeTransfer(0x0bB0ded1d868F1c0a50bD31c1ab5ab7b53c6BC20, 3 * 1000000000000000000000000); // Account_39
        _freezeTransfer(0x65ec9f30249065A1BD23a9c68c0Ee9Ead63b4A4d, 3 * 1000000000000000000000000); // Account_40
        _freezeTransfer(0x87Bdc03582deEeB84E00d3fcFd083B64DA77F471, 3 * 1000000000000000000000000); // Account_41
        _freezeTransfer(0x81382A0998191E2Dd8a7bB2B8875D4Ff6CAA31ff, 3 * 1000000000000000000000000); // Account_42
        _freezeTransfer(0x790069C894ebf518fB213F35b48C8ec5AAF81E62, 3 * 1000000000000000000000000); // Account_43
        _freezeTransfer(0xa3f1404851E8156DFb425eC0EB3D3d5ADF6c8Fc0, 3 * 1000000000000000000000000); // Account_44
        _freezeTransfer(0x11bA01dc4d93234D24681e1B19839D4560D17165, 3 * 1000000000000000000000000); // Account_45
        _freezeTransfer(0x211D495291534009B8D3fa491400aB66F1d6131b, 3 * 1000000000000000000000000); // Account_46
        _freezeTransfer(0x8c481AaF9a735F9a44Ac2ACFCFc3dE2e9B2f88f8, 3 * 1000000000000000000000000); // Account_47
        _freezeTransfer(0xd0BEF2Fb95193f429f0075e442938F5d829a33c8, 3 * 1000000000000000000000000); // Account_48
        _freezeTransfer(0x424cbEb619974ee79CaeBf6E9081347e64766705, 3 * 1000000000000000000000000); // Account_49
        _freezeTransfer(0x9e395cd98089F6589b90643Dde4a304cAe4dA61C, 3 * 1000000000000000000000000); // Account_50
        _freezeTransfer(0x3cDE6Df0906157107491ED17C79fF9218A50D7Dc, 3 * 1000000000000000000000000); // Account_51
        _freezeTransfer(0x419a98D46a368A1704278349803683abB2A9D78E, 3 * 1000000000000000000000000); // Account_52
        _freezeTransfer(0x106Db742344FBB96B46989417C151B781D1a4069, 3 * 1000000000000000000000000); // Account_53
        _freezeTransfer(0xE16b9E9De165DbecA18B657414136cF007458aF5, 3 * 1000000000000000000000000); // Account_54
        _freezeTransfer(0xee32C325A3E11759b290df213E83a257ff249936, 3 * 1000000000000000000000000); // Account_55
        _freezeTransfer(0x7d6F916b0E5BF7Ba7f11E60ed9c30fB71C4A5fE0, 3 * 1000000000000000000000000); // Account_56
        _freezeTransfer(0xCC684085585419100AE5010770557d5ad3F3CE58, 3 * 1000000000000000000000000); // Account_57
        _freezeTransfer(0xB47BE6d74C5bC66b53230D07fA62Fb888594418d, 3 * 1000000000000000000000000); // Account_58
        _freezeTransfer(0xf891555a1BF2525f6EBaC9b922b6118ca4215fdD, 3 * 1000000000000000000000000); // Account_59
        _freezeTransfer(0xE3124478A5ed8550eA85733a4543Dd128461b668, 3 * 1000000000000000000000000); // Account_60
        _freezeTransfer(0xc5836df630225112493fa04fa32B586f072d6298, 3 * 1000000000000000000000000); // Account_61
        _freezeTransfer(0x144a0543C93ce8Fb26c13EB619D7E934FA3eA734, 3 * 1000000000000000000000000); // Account_62
        _freezeTransfer(0x43731e24108E928984DcC63DE7affdF3a805FFb0, 3 * 1000000000000000000000000); // Account_63
        _freezeTransfer(0x49f7744Aa8B706Faf336a3ff4De37078714065BC, 3 * 1000000000000000000000000); // Account_64
        _freezeTransfer(0x1E55C7E97F0b5c162FC9C42Ced92C8e55053e093, 3 * 1000000000000000000000000); // Account_65
        _freezeTransfer(0x40b234009664590997D2F6Fde2f279fE56e8AaBC, 3 * 1000000000000000000000000); // Account_66
    }

    bool assignedTeam = false;
    // @notice assignTeamTokens assigns tokens to team members (79,901,000,000)
    // @notice tokens for team have their own supply
    function assignTeamTokens() public onlyOwnerAndDirector {
        require(!assignedTeam);
        assignedTeam = true;

        _teamTransfer(0x0A5f85C3d41892C934ae82BDbF17027A20717088,  101000000 * ReconToMicro); // Recon owner
        _teamTransfer(0x0f65e64662281D6D42eE6dEcb87CDB98fEAf6060,  100000000 * ReconToMicro); // Recon newOwner
        _teamTransfer(0x3Da2585FEbE344e52650d9174e7B1bf35C70D840,   50000000 * ReconToMicro); // Recon minter
        _teamTransfer(0xc083E68D962c2E062D2735B54804Bb5E1f367c1b,   50000000 * ReconToMicro); // Recon feeAccount
        _teamTransfer(0xF848332f5D902EFD874099458Bc8A53C8b7881B1,   50000000 * ReconToMicro); // Recon spender
        _teamTransfer(0x5f2D6766C6F3A7250CfD99d6b01380C432293F0c,   50000000 * ReconToMicro); // Recon recoveredAddress
        _teamTransfer(0x5f2D6766C6F3A7250CfD99d6b01380C432293F0c,  200000000 * ReconToMicro); // Proprity from ReconBank
        _teamTransfer(0xD974C2D74f0F352467ae2Da87fCc64491117e7ac,  200000000 * ReconToMicro); // Recon Manager
        _teamTransfer(0x5c4F791D0E0A2E75Ee34D62c16FB6D09328555fF, 5000000000 * ReconToMicro); // Recon Cash-in (B2B)
        _teamTransfer(0xeB479640A6D55374aF36896eCe6db7d92F390015, 5000000000 * ReconToMicro); // Recon Switch (C2C)
        _teamTransfer(0x77167D25Db87dc072399df433e450B00b8Ec105A, 7000000000 * ReconToMicro); // Recon Cash-out (B2C)
        _teamTransfer(0x5C6Fd84b961Cce03e027B0f8aE23c4A6e1195E90, 2000000000 * ReconToMicro); // Recon Investment
        _teamTransfer(0x86F427c5e05C29Fd4124746f6111c1a712C9B5c8, 2000000000 * ReconToMicro); // Recon Momentum
        _teamTransfer(0x1Ecb8dC0932AF3A3ba87e8bFE7eac3Cbe433B78B, 2000000000 * ReconToMicro); // Recon Reward
        _teamTransfer(0x7C31BeCa0290C35c8452b95eA462C988c4003Bb0, 1000000000 * ReconToMicro); // Recon Donate
        _teamTransfer(0x3a5326f9C9b3ff99e2e5011Aabec7b48B2e6A6A2, 4000000000 * ReconToMicro); // Recon Token
        _teamTransfer(0x5a27B07003ce50A80dbBc5512eA5BBd654790673, 4000000000 * ReconToMicro); // Recon Cash
        _teamTransfer(0xD580cF1002d0B4eF7d65dC9aC6a008230cE22692, 4000000000 * ReconToMicro); // Recon Gold
        _teamTransfer(0x9C83562Bf58083ab408E596A4bA4951a2b5724C9, 4000000000 * ReconToMicro); // Recon Card
        _teamTransfer(0x70E06c2Dd9568ECBae760CE2B61aC221C0c497F5, 2000000000 * ReconToMicro); // Recon Hardrive Wallet
        _teamTransfer(0x14bd2Aa04619658F517521adba7E5A17dfD2A3f0, 1000000000 * ReconToMicro); // Recoin Option
        _teamTransfer(0x9C3091a335383566d08cba374157Bdff5b8B034B,  100000000 * ReconToMicro); // Recon Promo
        _teamTransfer(0x3b6F53122903c40ef61441dB807f09D90D6F05c7, 1000000000 * ReconToMicro); // Recon patents
        _teamTransfer(0x7fb5EF151446Adb0B7D39B1902E45f06E11038F6, 1000000000 * ReconToMicro); // Recon Security & Legal Fees
        _teamTransfer(0x47BD87fa63Ce818584F050aFFECca0f1dfFd0564, 1000000000 * ReconToMicro); // ​Peer To Peer Networking Service
        _teamTransfer(0x83b3CD589Bd78aE65d7b338fF7DFc835cD9a8edD, 2000000000 * ReconToMicro); // Reconia
        _teamTransfer(0x6299496342fFd22B7191616fcD19CeC6537C2E8D, 8000000000 * ReconToMicro); // ​Recon Central Securities Depository (Recon Vault XtraStock)
        _teamTransfer(0x26aF11607Fad4FacF1fc44271aFA63Dbf2C22a87, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault SecurityStock)
        _teamTransfer(0x7E21203C5B4A6f98E4986f850dc37eBE9Ca19179, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault Advance Payment Stock)
        _teamTransfer(0x0bD212e88522b7F4C673fccBCc38558829337f71, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault PrivatStock)
        _teamTransfer(0x5b44e309408cE6E73B9f5869C9eeaCeeb8084DC8, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault Currency Insurance stock)
        _teamTransfer(0x48F2eFDE1c028792EbE7a870c55A860e40eb3573, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault NextStock)
        _teamTransfer(0x1fF3BE6f711C684F04Cf6adfD665Ce13D54CAC73, 4000000000 * ReconToMicro); // Recon Central Securities Depository (Recon Vault FuturStock)
    }

    // @nptice kycPassed is executed by backend and tells SC
    // that particular client has passed KYC
    mapping(address => bool) public kyc;
    mapping(address => address) public referral;
    function kycPassed(address _mem, address _ref) public onlyAdmin {
        kyc[_mem] = true;
        if (_ref == richardAddr || _ref == wuguAddr) {
            referral[_mem] = _ref;
        }
    }

    // mappings for implementing ERC20
    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;

    // mapping for implementing unlock mechanic
    mapping(address => uint) freezed;
    mapping(address => uint) teamFreezed;

    // ERC20 standard functions
    function totalSupply() public view returns (uint) {
        return _totalSupply;
    }

    function balanceOf(address tokenOwner) public view returns (uint balance) {
        return balances[tokenOwner];
    }

    function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }

    function _transfer(address _from, address _to, uint _tokens) private {
        balances[_from] = balances[_from].sub(_tokens);
        balances[_to] = balances[_to].add(_tokens);
        emit Transfer(_from, _to, _tokens);
    }

    function transfer(address _to, uint _tokens) public returns (bool success) {
        checkTransfer(msg.sender, _tokens);
        _transfer(msg.sender, _to, _tokens);
        return true;
    }

    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        checkTransfer(from, tokens);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
        _transfer(from, to, tokens);
        return true;
    }

    // @notice checkTransfer ensures that `from` can send only unlocked tokens
    // @notice this function is called for every transfer
    // We unlock PURCHASED and BONUS tokens in 13 stages:
    function checkTransfer(address from, uint tokens) public view {
        uint newBalance = balances[from].sub(tokens);
        uint total = 0;
        if (now < unlockDate5) {
            require(now >= unlockDate1);
            uint frzdPercent = 0;
            if (now < unlockDate2) {
                frzdPercent = 5;
            } else if (now < unlockDate3) {
                frzdPercent = 10;
            } else if (now < unlockDate4) {
                frzdPercent = 10;
            } else if (now < unlockDate5) {
                frzdPercent = 10;
            } else if (now < unlockDate6) {
                frzdPercent = 10;
            } else if (now < unlockDate7) {
                frzdPercent = 10;
            } else if (now < unlockDate8) {
                frzdPercent = 5;
            } else if (now < unlockDate9) {
                frzdPercent = 5;
            } else if (now < unlockDate10) {
                frzdPercent = 10;
            } else if (now < unlockDate11) {
                frzdPercent = 5;
            } else if (now < unlockDate12) {
                frzdPercent = 10;
            } else if (now < unlockDate13) {
                frzdPercent = 5;
            } else {
                frzdPercent = 5;
            }
            total = freezed[from].mul(frzdPercent).div(100);
            require(newBalance >= total);
        }

        if (now < teamUnlock4 && teamFreezed[from] > 0) {
            uint p = 0;
            if (now < teamUnlock1) {
                p = 100;
            } else if (now < teamUnlock2) {
                p = 75;
            } else if (now < teamUnlock3) {
                p = 50;
            } else if (now < teamUnlock4) {
                p = 25;
            }
            total = total.add(teamFreezed[from].mul(p).div(100));
            require(newBalance >= total);
        }
    }

    // @return ($ received, ETH received, RECON sold)
    function ICOStatus() public view returns (uint usd, uint eth, uint recon) {
        usd = presaleSold.mul(12).div(10**20) + crowdsaleSold.mul(16).div(10**20);
        usd = usd.add(preicoUSD); // pre-ico tokens

        return (usd, ethSold + preicoUSD.mul(10**8).div(ethRate), presaleSold + crowdsaleSold);
    }

    function checkICOStatus() public view returns(bool) {
        uint eth;
        uint recon;

        (, eth, recon) = ICOStatus();

        uint dollarsRecvd = eth.mul(ethRate).div(10**8);

        // 26 228 800$
        return dollarsRecvd >= 25228966 || (recon == presaleSupply + crowdsaleSupply) || now > crowdsaleEndTime;
    }

    bool icoClosed = false;
    function closeICO() public onlyOwner {
        require(!icoClosed);
        icoClosed = checkICOStatus();
    }

    // @notice by agreement, we can transfer $4.8M from bank
    // after softcap is reached.
    // @param _to wallet to send RECON to
    // @param  _usd amount of dollars which is withdrawn
    uint bonusTransferred = 0;
    uint constant maxUSD = 4800000;
    function transferBonus(address _to, uint _usd) public onlyOwner {
        bonusTransferred = bonusTransferred.add(_usd);
        require(bonusTransferred <= maxUSD);

        uint recon = _usd.mul(100).mul(ReconToMicro).div(12); // presale tariff
        presaleSold = presaleSold.add(recon);
        require(presaleSold <= presaleSupply);
        ethSold = ethSold.add(_usd.mul(10**8).div(ethRate));

        _freezeTransfer(_to, recon);
    }

    // @notice extend crowdsale for 2 weeks
    function prolongCrowdsale() public onlyOwnerAndDirector {
        require(now < crowdsaleEndTime);
        crowdsaleEndTime = crowdsaleHardEndTime;
    }

    // 100 000 000 Ether in dollars
    uint public ethRate = 0;
    uint public ethRateMax = 0;
    uint public ethLastUpdate = 0;
    function setETHRate(uint _rate) public onlyAdmin {
        require(ethRateMax == 0 || _rate < ethRateMax);
        ethRate = _rate;
        ethLastUpdate = now;
    }

    // 100 000 000 BTC in dollars
    uint public btcRate = 0;
    uint public btcRateMax = 0;
    uint public btcLastUpdate;
    function setBTCRate(uint _rate) public onlyAdmin {
        require(btcRateMax == 0 || _rate < btcRateMax);
        btcRate = _rate;
        btcLastUpdate = now;
    }

    // @notice setMaxRate sets max rate for both BTC/ETH to soften
    // negative consequences in case our backend gots hacked.
    function setMaxRate(uint ethMax, uint btcMax) public onlyOwnerAndDirector {
        ethRateMax = ethMax;
        btcRateMax = btcMax;
    }

    // @notice _sellPresale checks RECON purchases during crowdsale
    function _sellPresale(uint recon) private {
        require(recon >= bonusLevel0.mul(9950).div(10000));
        presaleSold = presaleSold.add(recon);
        require(presaleSold <= presaleSupply);
    }

    // @notice _sellCrowd checks RECON purchases during crowdsale
    function _sellCrowd(uint recon, address _to) private {
        require(recon >= crowdsaleMinUSD);

        if (crowdsaleSold.add(recon) <= crowdsaleSupply) {
            crowdsaleSold = crowdsaleSold.add(recon);
        } else {
            presaleSold = presaleSold.add(crowdsaleSold).add(recon).sub(crowdsaleSupply);
            require(presaleSold <= presaleSupply);
            crowdsaleSold = crowdsaleSupply;
        }

        if (now < crowdsaleStartTime + 3 days) {
            if (whitemap[_to] >= recon) {
                whitemap[_to] -= recon;
                whitelistTokens -= recon;
            } else {
                require(crowdsaleSupply.add(presaleSupply).sub(presaleSold) >= crowdsaleSold.add(whitelistTokens));
            }
        }
    }

    // @notice addInvestorBonusInPercent is used for sending bonuses for big investors in %
    function addInvestorBonusInPercent(address _to, uint8 p) public onlyOwner {
        require(p > 0 && p <= 5);
        uint bonus = balances[_to].mul(p).div(100);

        investorGiven = investorGiven.add(bonus);
        require(investorGiven <= investorSupply);

        _freezeTransfer(_to, bonus);
    }

    // @notice addInvestorBonusInTokens is used for sending bonuses for big investors in tokens
    function addInvestorBonusInTokens(address _to, uint tokens) public onlyOwner {
        _freezeTransfer(_to, tokens);

        investorGiven = investorGiven.add(tokens);
        require(investorGiven <= investorSupply);
    }

    function () payable public {
        purchaseWithETH(msg.sender);
    }

    // @notice _freezeTranfer perform actual tokens transfer which
    // will be freezed (see also checkTransfer() )
    function _freezeTransfer(address _to, uint recon) private {
        _transfer(owner, _to, recon);
        freezed[_to] = freezed[_to].add(recon);
    }

    // @notice _freezeTranfer perform actual tokens transfer which
    // will be freezed (see also checkTransfer() )
    function _teamTransfer(address _to, uint recon) private {
        _transfer(owner, _to, recon);
        teamFreezed[_to] = teamFreezed[_to].add(recon);
    }

    address public constant wuguAddr = 0x0d340F1344a262c13485e419860cb6c4d8Ec9C6e;
    address public constant richardAddr = 0x49BE16e7FECb14B82b4f661D9a0426F810ED7127;
    mapping(address => address[]) promoterClients;
    mapping(address => mapping(address => uint)) promoterBonus;

    // @notice withdrawPromoter transfers back to promoter
    // all bonuses accumulated to current moment
    function withdrawPromoter() public {
        address _to = msg.sender;
        require(_to == wuguAddr || _to == richardAddr);

        uint usd;
        (usd,,) = ICOStatus();

        // USD received - 5% must be more than softcap
        require(usd.mul(95).div(100) >= softcapUSD);

        uint bonus = 0;
        address[] memory clients = promoterClients[_to];
        for(uint i = 0; i < clients.length; i++) {
            if (kyc[clients[i]]) {
                uint num = promoterBonus[_to][clients[i]];
                delete promoterBonus[_to][clients[i]];
                bonus += num;
            }
        }

        _to.transfer(bonus);
    }

    // @notice cashBack will be used in case of failed ICO
    // All partitipants can receive their ETH back
    function cashBack(address _to) public {
        uint usd;
        (usd,,) = ICOStatus();

        // ICO fails if crowd-sale is ended and we have not yet reached soft-cap
        require(now > crowdsaleEndTime && usd < softcapUSD);
        require(ethSent[_to] > 0);

        delete ethSent[_to];

        _to.transfer(ethSent[_to]);
    }

    // @notice stores amount of ETH received by SC
    mapping(address => uint) ethSent;

    function purchaseWithETH(address _to) payable public {
        purchaseWithPromoter(_to, referral[msg.sender]);
    }

    // @notice purchases tokens, which a send to `_to` with 5% returned to `_ref`
    // @notice 5% return must work only on crowdsale
    function purchaseWithPromoter(address _to, address _ref) payable public {
        require(now >= presaleStartTime && now <= crowdsaleEndTime);

        require(!icoClosed);

        uint _wei = msg.value;
        uint recon;

        ethSent[msg.sender] = ethSent[msg.sender].add(_wei);
        ethSold = ethSold.add(_wei);

        // accept payment on presale only if it is more than 9997$
        // actual check is performed in _sellPresale
        if (now < crowdsaleStartTime || approvedInvestors[msg.sender]) {
            require(kyc[msg.sender]);
            recon = _wei.mul(ethRate).div(75000000); // 1 RECON = 0.75 $ on presale

            require(now < crowdsaleStartTime || recon >= bonusLevel100);

            _sellPresale(recon);

            // we have only 2 recognized promoters
            if (_ref == wuguAddr || _ref == richardAddr) {
                promoterClients[_ref].push(_to);
                promoterBonus[_ref][_to] = _wei.mul(5).div(100);
            }
        } else {
            recon = _wei.mul(ethRate).div(10000000); // 1 RECON = 1.00 $ on crowd-sale
            _sellCrowd(recon, _to);
        }

        _freezeTransfer(_to, recon);
    }

    // @notice purchaseWithBTC is called from backend, where we convert
    // BTC to ETH, and then assign tokens to purchaser, using BTC / $ exchange rate.
    function purchaseWithBTC(address _to, uint _satoshi, uint _wei) public onlyAdmin {
        require(now >= presaleStartTime && now <= crowdsaleEndTime);

        require(!icoClosed);

        ethSold = ethSold.add(_wei);

        uint recon;
        // accept payment on presale only if it is more than 9997$
        // actual check is performed in _sellPresale
        if (now < crowdsaleStartTime || approvedInvestors[msg.sender]) {
            require(kyc[msg.sender]);
            recon = _satoshi.mul(btcRate.mul(10000)).div(75); // 1 RECON = 0.75 $ on presale

            require(now < crowdsaleStartTime || recon >= bonusLevel100);

            _sellPresale(recon);
        } else {
            recon = _satoshi.mul(btcRate.mul(10000)).div(100); // 1 RECON = 1.00 $ on presale
            _sellCrowd(recon, _to);
        }

        _freezeTransfer(_to, recon);
    }

    // @notice withdrawFunds is called to send team bonuses after
    // then end of the ICO
    bool withdrawCalled = false;
    function withdrawFunds() public onlyOwner {
        require(icoClosed && now >= teamETHUnlock1);

        require(!withdrawCalled);
        withdrawCalled = true;

        uint eth;
        (,eth,) = ICOStatus();

        // pre-ico tokens are not in ethSold
        uint minus = bonusTransferred.mul(10**8).div(ethRate);
        uint team = ethSold.sub(minus);

        team = team.mul(15).div(100);

        uint ownerETH = 0;
        uint teamETH = 0;
        if (address(this).balance >= team) {
            teamETH = team;
            ownerETH = address(this).balance.sub(teamETH);
        } else {
            teamETH = address(this).balance;
        }

        teamETH1 = teamETH.div(3);
        teamETH2 = teamETH.div(3);
        teamETH3 = teamETH.sub(teamETH1).sub(teamETH2);

        // TODO multisig
        address(0xf14B65F1589B8bC085578BcF68f09653D8F6abA8).transfer(ownerETH);
    }

    uint teamETH1 = 0;
    uint teamETH2 = 0;
    uint teamETH3 = 0;
    function withdrawTeam() public {
        require(now >= teamETHUnlock1);

        uint amount = 0;
        if (now < teamETHUnlock2) {
            amount = teamETH1;
            teamETH1 = 0;
        } else if (now < teamETHUnlock3) {
            amount = teamETH1 + teamETH2;
            teamETH1 = 0;
            teamETH2 = 0;
        } else {
            amount = teamETH1 + teamETH2 + teamETH3;
            teamETH1 = 0;
            teamETH2 = 0;
            teamETH3 = 0;
        }

        address(0x5c4F791D0E0A2E75Ee34D62c16FB6D09328555fF).transfer(amount.mul(6).div(100)); // Recon Cash-in (B2B)
        address(0xeB479640A6D55374aF36896eCe6db7d92F390015).transfer(amount.mul(6).div(100)); // Recon Switch (C2C)
        address(0x77167D25Db87dc072399df433e450B00b8Ec105A).transfer(amount.mul(6).div(100)); // Recon Cash-out (B2C)
        address(0x1Ecb8dC0932AF3A3ba87e8bFE7eac3Cbe433B78B).transfer(amount.mul(2).div(100)); // Recon Reward
        address(0x7C31BeCa0290C35c8452b95eA462C988c4003Bb0).transfer(amount.mul(2).div(100)); // Recon Donate

        amount = amount.mul(78).div(100);

        address(0x3a5326f9C9b3ff99e2e5011Aabec7b48B2e6A6A2).transfer(amount.mul(uint(255).mul(100).div(96)).div(1000)); // Recon Token
        address(0x5a27B07003ce50A80dbBc5512eA5BBd654790673).transfer(amount.mul(uint(185).mul(100).div(96)).div(1000)); // Recon Cash
        address(0xD580cF1002d0B4eF7d65dC9aC6a008230cE22692).transfer(amount.mul(uint(25).mul(100).div(96)).div(1000));  // Recon Gold
        address(0x9C83562Bf58083ab408E596A4bA4951a2b5724C9).transfer(amount.mul(uint(250).mul(100).div(96)).div(1000)); // Recon Card
        address(0x70E06c2Dd9568ECBae760CE2B61aC221C0c497F5).transfer(amount.mul(uint(245).mul(100).div(96)).div(1000)); // Recon Hardrive Wallet
    }

    // @notice doAirdrop is called when we launch airdrop.
    // @notice airdrop tokens has their own supply.
    uint dropped = 0;
    function doAirdrop(address[] members, uint[] tokens) public onlyOwnerAndDirector {
        require(members.length == tokens.length);

        for(uint i = 0; i < members.length; i++) {
            _freezeTransfer(members[i], tokens[i]);
            dropped = dropped.add(tokens[i]);
        }
        require(dropped <= bountySupply);
    }

    mapping(address => uint) public whitemap;
    uint public whitelistTokens = 0;
    // @notice addWhitelistMember is used to whitelist participant.
    // This means, that for the first 3 days of crowd-sale `_tokens` RECON
    // will be reserved for him.
    function addWhitelistMember(address[] _mem, uint[] _tokens) public onlyAdmin {
        require(_mem.length == _tokens.length);
        for(uint i = 0; i < _mem.length; i++) {
            whitelistTokens = whitelistTokens.sub(whitemap[_mem[i]]).add(_tokens[i]);
            whitemap[_mem[i]] = _tokens[i];
        }
    }

    uint public adviserSold = 0;
    // @notice transferAdviser is called to send tokens to advisers.
    // @notice adviser tokens have their own supply
    function transferAdviser(address[] _adv, uint[] _tokens) public onlyOwnerAndDirector {
        require(_adv.length == _tokens.length);
        for (uint i = 0; i < _adv.length; i++) {
            adviserSold = adviserSold.add(_tokens[i]);
            _freezeTransfer(_adv[i], _tokens[i]);
        }
        require(adviserSold <= adviserSupply);
    }

    mapping(address => bool) approvedInvestors;
    function approveInvestor(address _addr) public onlyOwner {
        approvedInvestors[_addr] = true;
    }
}
function addInvestorBonusInTokens(address _to, uint tokens) public onlyOwner {
        _freezeTransfer(_to, tokens);

        investorGiven = investorGiven.add(tokens);
        require(investorGiven <= investorSupply);
    }
// @notice addInvestorBonusInTokens is used for sending bonuses for big investors in tokens