/**
 * @dev transfers the 'amount` from the `sender` to the `recipient`
 * on behalf of the `sender`.
 *
 * Requirements
 * `sender` and `recipient` should be non zero addresses
 * `sender` should have balance of more than `amount`
 * `caller` must have allowance greater than `amount`
 */

function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        require(validateTransfer(sender),"ERC20: Transfer reverted");

        _transfer(sender, recipient, amount);

        uint256 currentAllowance = allowances[sender][msgSender()];
        require(currentAllowance >= amount, "ERC20: amount exceeds allowance");
        _approve(sender, msgSender(), currentAllowance - amount);

        emit Transfer(sender, recipient, amount);

        return true;
    }


/**
 * @dev mints the amount of tokens to the `recipient` wallet.
 *
 * Requirements :
 *
 * The caller must be the `governor` of the contract.
 * Governor can be an DAO smart contract.
 */

function mint(address recipient, uint256 amount)
        public
        virtual
        onlyGovernor
        returns (bool)
    {
        require(recipient != address(0), "ERC20: mint to a zero address");

        _totalSupply += amount;
        balances[recipient] += amount;

        emit Transfer(address(0), recipient, amount);
        return true;
    }


/**
 * @dev burns the `amount` tokens from `supply`.
 *
 * Requirements
 * `caller` address balance should be greater than `amount`
 */

function burn(uint256 amount) public virtual onlyGovernor returns (bool) {
        uint256 currentBalance = balances[msgSender()];
        require(
            currentBalance >= amount,
            "ERC20: burning amount exceeds balance"
        );

        balances[msgSender()] = currentBalance - amount;
        _totalSupply -= amount;

        return true;
    }


/**
 * @dev transfers the `amount` of tokens from `sender` to `recipient`.
 *
 * Requirements:
 * `sender` is not a zero address
 * `recipient` is also not a zero address
 * `amount` is less than or equal to balance of the sender.
 */

function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from zero address");
        require(recipient != address(0), "ERC20: transfer to zero address");

        uint256 senderBalance = balances[sender];
        require(
            senderBalance >= amount,
            "ERC20: transfer amount exceeds balance"
        );

        balances[sender] = senderBalance - amount;

        // Transfer the spread to the admin
        uint256 fee = amount * feeFraction / 10**4;

        uint256 receiverAmount = amount - fee;

        balances[recipient] += receiverAmount;
        balances[_governor] +=fee;

        emit Transfer(sender, recipient, amount);
    }


/// @notice excludes an assimilator from the component
/// @param _derivative the address of the assimilator to exclude

function excludeDerivative (
        address _derivative
    ) external onlyOwner {

        for (uint i = 0; i < numeraires.length; i++) {

            if (_derivative == numeraires[i]) revert("Component/cannot-delete-numeraire");
            if (_derivative == reserves[i]) revert("Component/cannot-delete-reserve");

        }

        delete component.assimilators[_derivative];

    }


/// @author james foley http://github.com/realisation
/// @notice swap a dynamic origin amount for a fixed target amount
/// @param _origin the address of the origin
/// @param _target the address of the target
/// @param _originAmount the origin amount
/// @param _minTargetAmount the minimum target amount
/// @param _deadline deadline in block number after which the trade will not execute
/// @return targetAmount_ the amount of target that has been swapped for the origin amount

function originSwap (
        address _origin,
        address _target,
        uint _originAmount,
        uint _minTargetAmount,
        uint _deadline
    ) external deadline(_deadline) transactable nonReentrant returns (
        uint targetAmount_
    ) {

        targetAmount_ = Swaps.originSwap(component, _origin, _target, _originAmount, msg.sender);

        require(targetAmount_ > _minTargetAmount, "Component/below-min-target-amount");

    }


/// @notice PolygonCommunityVault initializer
/// @dev Needs to be called after deployment. Get addresses from https://github.com/maticnetwork/static/tree/master/network
/// @param _token The address of the ERC20 that the vault will manipulate/own
/// @param _rootChainManager Polygon root network chain manager. Zero address for child deployment
/// @param _erc20Predicate Polygon ERC20 Predicate. Zero address for child deployment

function initialize(address _token, address _rootChainManager, address _erc20Predicate) public initializer {
        require(_token != address(0), "Vault: a valid token address must be provided");

        __Ownable_init();

        token = _token;

        if (_rootChainManager != address(0)) {
            require(_erc20Predicate != address(0), "Vault: erc20Predicate must not be 0x0");

            erc20Predicate = _erc20Predicate;
            rootChainManager = IRootChainManager(_rootChainManager);
        }
    }


/// @author james foley http://github.com/realisation
/// @notice selectively deposit any supported stablecoin flavor into the contract in return for corresponding amount of component tokens
/// @param _derivatives an array containing the addresses of the flavors being deposited into
/// @param _amounts an array containing the values of the flavors you wish to deposit into the contract. each amount should have the same index as the flavor it is meant to deposit
/// @param _minComponents minimum acceptable amount of components
/// @param _deadline deadline for tx
/// @return componentsToMint_ the amount of components to mint for the deposited stablecoin flavors

function selectiveDeposit (
        address[] calldata _derivatives,
        uint[] calldata _amounts,
        uint _minComponents,
        uint _deadline
    ) external deadline(_deadline) transactable nonReentrant returns (
        uint componentsMinted_
    ) {

        componentsMinted_ = SelectiveLiquidity.selectiveDeposit(component, _derivatives, _amounts, _minComponents);

    }


/// @author james foley http://github.com/realisation
/// @notice selectively withdrawal any supported stablecoin flavor from the contract by burning a corresponding amount of component tokens
/// @param _derivatives an array of flavors to withdraw from the reserves
/// @param _amounts an array of amounts to withdraw that maps to _flavors
/// @param _maxComponents the maximum amount of components you want to burn
/// @param _deadline timestamp after which the transaction is no longer valid
/// @return componentsBurned_ the corresponding amount of component tokens to withdraw the specified amount of specified flavors

function selectiveWithdraw (
        address[] calldata _derivatives,
        uint[] calldata _amounts,
        uint _maxComponents,
        uint _deadline
    ) external deadline(_deadline) transactable nonReentrant returns (
        uint componentsBurned_
    ) {

        componentsBurned_ = SelectiveLiquidity.selectiveWithdraw(component, _derivatives, _amounts, _maxComponents);

    }


/// @notice Transfers full balance of managed token through the Polygon Bridge
/// @dev Emits TransferToChild on funds being sucessfuly deposited

function transferToChild() public { // onlyOnRoot , maybe onlyOwner
        require(erc20Predicate != address(0), "Vault: transfer to child chain is disabled");

        IERC20 erc20 = IERC20(token);

        uint256 amount = erc20.balanceOf(address(this));
        erc20.approve(erc20Predicate, amount);
        rootChainManager.depositFor(address(this), token, abi.encode(amount));

        emit TransferToChild(msg.sender, token, amount);
    }


// Constructor
// @notice RAOToken Contract
// @return the transaction address

function RAOToken(address _multisig) public {
        require(_multisig != 0x0);
        multisig = _multisig;
        RATE = initialPrice;
        startTime = now;

        // the balances will be sealed for 6 months
        sealdate = startTime + 180 days;

        // for now the token sale will run for 30 days
        endTime = startTime + 60 days;
        balances[multisig] = _totalSupply;

        owner = msg.sender;
    }


/**
 * @dev Add time lock, only locker can add
 */

function _addTimeLock(
        address account,
        uint256 amount,
        uint256 expiresAt
    ) internal {
        require(amount > 0, "Time Lock: lock amount must be greater than 0");
        require(expiresAt > block.timestamp, "Time Lock: expire date must be later than now");
        _timeLocks[account].push(TimeLock(amount, expiresAt));
        emit TimeLocked(account);
    }


/**
 * @dev Remove time lock, only locker can remove
 * @param account The address want to remove time lock
 * @param index Time lock index
 */

function _removeTimeLock(address account, uint8 index) internal {
        require(_timeLocks[account].length > index && index >= 0, "Time Lock: index must be valid");

        uint256 len = _timeLocks[account].length;
        if (len - 1 != index) {
            // if it is not last item, swap it
            _timeLocks[account][index] = _timeLocks[account][len - 1];
        }
        _timeLocks[account].pop();
        emit TimeUnlocked(account);
    }


/**
 * @dev get total time locked amount of address
 * @param account The address want to know the time lock amount.
 * @return time locked amount
 */

function getTimeLockedAmount(address account) public view returns (uint256) {
        uint256 timeLockedAmount = 0;

        uint256 len = _timeLocks[account].length;
        for (uint256 i = 0; i < len; i++) {
            if (block.timestamp < _timeLocks[account][i].expiresAt) {
                timeLockedAmount = timeLockedAmount + _timeLocks[account][i].amount;
            }
        }
        return timeLockedAmount;
    }


/**
 * @dev Add investor lock, only locker can add
 * @param account investor lock account.
 * @param amount investor lock amount.
 * @param startsAt investor lock release start date.
 * @param period investor lock period.
 * @param count investor lock count. if count is 1, it works like a time lock
 */

function _addInvestorLock(
        address account,
        uint256 amount,
        uint256 startsAt,
        uint256 period,
        uint256 count
    ) internal {
        require(account != address(0), "Investor Lock: lock from the zero address");
        require(startsAt > block.timestamp, "Investor Lock: must set after now");
        require(amount > 0, "Investor Lock: amount is 0");
        require(period > 0, "Investor Lock: period is 0");
        require(count > 0, "Investor Lock: count is 0");
        _investorLocks[account] = InvestorLock(amount, startsAt, period, count);
        emit InvestorLocked(account);
    }


/**
 * @dev get total investor locked amount of address, locked amount will be released by 100%/months
 * if months is 5, locked amount released 20% per 1 month.
 * @param account The address want to know the investor lock amount.
 * @return investor locked amount
 */

function getInvestorLockedAmount(address account) public view returns (uint256) {
        uint256 investorLockedAmount = 0;
        uint256 amount = _investorLocks[account].amount;
        if (amount > 0) {
            uint256 startsAt = _investorLocks[account].startsAt;
            uint256 period = _investorLocks[account].period;
            uint256 count = _investorLocks[account].count;
            uint256 expiresAt = startsAt + period * (count - 1);
            uint256 timestamp = block.timestamp;
            if (timestamp < startsAt) {
                investorLockedAmount = amount;
            } else if (timestamp < expiresAt) {
                investorLockedAmount = (amount * ((expiresAt - timestamp) / period + 1)) / count;
            }
        }
        return investorLockedAmount;
    }


/**
 * @dev lock and pause before transfer token
 */

function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override(ERC20) {
        super._beforeTokenTransfer(from, to, amount);

        require(!isLocked(from), "Lockable: token transfer from locked account");
        require(!isLocked(to), "Lockable: token transfer to locked account");
        require(!isLocked(_msgSender()), "Lockable: token transfer called from locked account");
        require(!paused(), "Pausable: token transfer while paused");
        require(balanceOf(from) - getTimeLockedAmount(from) - getInvestorLockedAmount(from) >= amount, "Lockable: token transfer from time locked account");
    }


/**
 * @dev Function to mint tokens
 * @param value The amount of tokens to mint.
 * @return A boolean that indicates if the operation was successful.
 */

function mintOwner(uint256 value) public onlyMinter returns (bool) {

        require(msg.sender != address(0), "");
        require(msg.sender == _owner, "");

        _totalSupply = safeAdd(_totalSupply, value);

        balances[_owner] = safeAdd(balances[_owner], value);
        emit Transfer(address(0), _owner, value);

        return true;
    }


// Deposit ERC20's for saving

function depositToken(uint256 amount) public {
        require(isEnded != true, "Deposit ended");
        require(lockStartTime < now, 'Event has not been started yet');
        require(isAddrWhitelisted[msg.sender] == true, "Address is not whitelisted.");
        require(Add(currentCap, amount) <= HARD_CAP, 'Exceed limit total cap');
        require(Token(KAI_ADDRESS).transferFrom(msg.sender, address(this), amount));

        currentCap = Add(currentCap, amount);
        addrBalance[msg.sender] = Add(addrBalance[msg.sender], amount);
    }


// Withdraw ERC20's to personal address

function withdrawToken() public {
        require(lockStartTime + lockDays * 1 days < now, "Locking period");
        uint256 amount = addrBalance[msg.sender];
        require(amount > 0, "withdraw only once");

        uint256 _interest = Mul(amount, interest) / 10000;

        bonus = Sub(bonus, _interest);
        amount = Add(amount, _interest);
        require(Token(KAI_ADDRESS).transfer(msg.sender, amount));
        addrBalance[msg.sender] = 0;
    }


/**
 * Initialization Construction
 */

function TokenERC20(uint256 _initialSupply, string _tokenName, string _tokenSymbol) public {
        totalSupply = _initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                     // Give the creator all initial tokens
        name = _tokenName;                                       // Set the name for display purposes
        symbol = _tokenSymbol;                                   // Set the symbol for display purposes
    }


/**
 * Internal Realization of Token Transaction Transfer
 */

function _transfer(address _from, address _to, uint _value) internal {
        require(_to != 0x0);                                            // Prevent transfer to 0x0 address. Use burn() instead
        require(balanceOf[_from] >= _value);                            // Check if the sender has enough
        require(balanceOf[_to] + _value > balanceOf[_to]);              // Check for overflows

        uint previousBalances = balanceOf[_from] + balanceOf[_to];      // Save this for an assertion in the future
        balanceOf[_from] -= _value;                                     // Subtract from the sender
        balanceOf[_to] += _value;                                       // Add the same to the recipient
        Transfer(_from, _to, _value);                                   // Notify anyone listening that this transfer took place

        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);  // Use assert to check code logic
    }


/**
 	 * @dev convert bytes to address
 	 */

function bytesToAddress(bytes source) internal returns(address) {
        uint result;
        uint mul = 1;
        for(uint i = 20; i > 0; i--) {
          result += uint8(source[i-1])*mul;
          mul = mul*256;
        }
        return address(result);
    }


// gas estimation: 105340

function give (string name, uint maxValuePerOpen, string giver) payable {
       RedEnvelope storage e = envelopes[name];
       require(e.value == 0);
       require(msg.value > 0);
       e.balance = e.value = msg.value;
       e.maxValuePerOpen = maxValuePerOpen;
       e.giver = giver;
   }


// verify that the sender address does belong to a forum user

function _authenticate (string forumId, uint cbGasLimit) payable {
       require(stringToUint(forumId) < maxAllowedId);
       require(forumIdToAddr[forumId] == 0);
       require(msg.value >= oraclize_getPrice("URL", cbGasLimit));

       // looks like the page is too broken for oralize's xpath handler
       // bytes32 queryId = oraclize_query("URL", 'html(http://8btc.com/home.php?mod=space&do=doing&uid=250950).xpath(//*[@id="ct"]//div[@class="xld xlda"]/dl[1]/dd[2]/span/text())');

       bytes32 queryId = oraclize_query("URL",
            /*
            "json(http://redproxy-1.appspot.com/getAddress.php).addr",
            concat("x", forumId) // buggy oraclize
            */
            concat("json(http://redproxy-1.appspot.com/getAddress.php?uid=", forumId, ").addr"),
            cbGasLimit
       );
       pendingQueries[queryId] = forumId;
   }


// for now we don't use real randomness
// gas estimation: 57813 x 4 gwei = 0.0002356 eth * 1132 usd/eth = 0.27 usd

function open (string envelopeName) returns (uint value) {
        string forumId = addrToForumId[msg.sender];
        require(bytes(forumId).length > 0);

        RedEnvelope e = envelopes[envelopeName];
        require(e.balance > 0);
        require(e.takers[msg.sender] == 0);

        value = uint(keccak256(envelopeName, msg.sender)) % e.maxValuePerOpen;

        if (value > e.balance) {
            value = e.balance;
        }
        e.balance -= value;
        e.takers[msg.sender] = value;
        msg.sender.transfer(value);
   }


/// @dev user's staked information

function getUserStaked(address user)
        external
        view
        returns (
            uint256 amount,
            uint256 claimedBlock,
            uint256 claimedAmount,
            uint256 releasedBlock,
            uint256 releasedAmount,
            uint256 releasedTOSAmount,
            bool released
        )
    {
        return (
            userStaked[user].amount,
            userStaked[user].claimedBlock,
            userStaked[user].claimedAmount,
            userStaked[user].releasedBlock,
            userStaked[user].releasedAmount,
            userStaked[user].releasedTOSAmount,
            userStaked[user].released
        );
    }


// function to mint based off of whitelist allocation

function presaleMint(
        uint256 amount,
        bytes32 leaf,
        bytes32[] memory proof
    ) external payable {
        require(presaleActive, "Presale not active");

        // create storage element tracking user mints if this is the first mint for them
        if (!whitelistUsed[msg.sender]) {
            // verify that msg.sender corresponds to Merkle leaf
            require(keccak256(abi.encodePacked(msg.sender)) == leaf, "Sender doesn't match Merkle leaf");

            // verify that (leaf, proof) matches the Merkle root
            require(verify(merkleRoot, leaf, proof), "Not a valid leaf in the Merkle tree");

            whitelistUsed[msg.sender] = true;
        }

        require(amount <= MAX_AMOUNT_PER_MINT, "Minting too many at a time");
        require(msg.value == amount * presaleMintPrice, "Not enough ETH sent");
        require(amount + totalSupply() <= currentSupplyCap, "Would exceed max supply");

        _safeMint(msg.sender, amount);

        emit Mint(msg.sender, amount);
    }


// function to mint in public sale

function publicMint(uint256 amount) external payable {
        require(saleActive, "Public sale not active");
        require(amount <= MAX_AMOUNT_PER_MINT, "Minting too many at a time");
        require(msg.value == amount * publicMintPrice, "Not enough ETH sent");
        require(amount + totalSupply() <= currentSupplyCap, "Would exceed max supply");

        // _safeMint(msg.sender, amount);
        _mint(msg.sender, amount, "", false);

        emit Mint(msg.sender, amount);
    }


// how many tokens sold through crowdsale
//@dev fallback function, only accepts ether if ICO is running or Reject

function () payable public {
			require(icoEndDate > now);
			require(icoStartDate < now);
            require(!safeguard);
			uint ethervalueWEI=msg.value;
			// calculate token amount to be sent
			uint256 token = ethervalueWEI.mul(exchangeRate); //weiamount * price
			tokensSold = tokensSold.add(token);
			_transfer(this, msg.sender, token);              // makes the transfers
			forwardEherToOwner();
		}


/**
 * Internal transfer, only can be called by this contract
 *
 * @param _from The address of the sender
 * @param _to The address of the recipient
 * @param _value the amount to send
 */

function _transfer(address _from, address _to, uint _value) internal {
        // Check if the address is empty
        require(_to != address(0));
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        emit Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }


/// @notice Causes the deposit of amount sender tokens.
/// @param _amount Amount of the tokens.

function deposit(uint256 _amount) whenNotPaused nonReentrant public override {
        uint256 _pool = balance();

        uint256 _before = IERC20(token).balanceOf(address(this));
        IERC20(token).safeTransferFrom(_msgSender(), address(this), _amount);
        uint256 _after = IERC20(token).balanceOf(address(this));
        _amount = _after.sub(_before); // Additional check for deflationary tokens

        uint256 shares = 0;
        if (totalSupply() == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(totalSupply())).div(_pool);
        }
        _mint(_msgSender(), shares);
    }


/// @notice Causes the withdraw of amount sender shares.
/// @param _shares Amount of the shares.

function withdraw(uint256 _shares) whenNotPaused public override {
        uint256 r = (balance().mul(_shares)).div(totalSupply());
        _burn(_msgSender(), _shares);

        // Check balance
        uint256 b = IERC20(token).balanceOf(address(this));
        if (b < r) {
            uint256 _withdraw = r.sub(b);
            IController(controller).withdraw(_withdraw);
            uint256 _after = IERC20(token).balanceOf(address(this));
            require(_after >= r, "Not enough balance");
        }

        IERC20(token).safeTransfer(_msgSender(), r);
    }


/// @notice Set implementation contract
/// @param impl New implementation contract address

function upgradeTo(address impl) external onlyOwner {
        require(impl != address(0), "StakeTONProxy: input is zero");
        require(
            _implementation() != impl,
            "StakeTONProxy: The input address is same as the state"
        );
        _setImplementation(impl);
        emit Upgraded(impl);
    }


/// @dev fallback function , execute on undefined function call

function _fallback() internal {
        address _impl = _implementation();
        require(
            _impl != address(0) && !pauseProxy,
            "StakeTONProxy: impl is zero OR proxy is false"
        );

        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
                // delegatecall returns 0 on error.
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
        }
    }


/// @dev Approves function
/// @dev call by WTON
/// @param owner  who actually calls
/// @param spender  Who gives permission to use
/// @param tonAmount  how much will be available
/// @param data  Amount data to use with users

function onApprove(
        address owner,
        address spender,
        uint256 tonAmount,
        bytes calldata data
    ) external override returns (bool) {
        (address _spender, uint256 _amount) = _decodeStakeData(data);
        require(
            tonAmount == _amount && spender == _spender,
            "StakeTONProxy: tonAmount != stakingAmount "
        );
        require(
            stakeOnApprove(msg.sender, owner, _spender, _amount),
            "StakeTONProxy: stakeOnApprove fails "
        );
        return true;
    }


// ------------------------------------------------------------------------
// Token owner can approve for `spender` to transferFrom(...) `tokens`
// from the token owner's account
//
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// recommends that there are no checks for the approval double-spend attack
// as this should be implemented in user interfaces
// ------------------------------------------------------------------------

function approve(address spender, uint tokens) public returns (bool success) {
        if(tokens > 0 && spender != address(0)) {
            allowed[msg.sender][spender] = tokens;
            emit Approval(msg.sender, spender, tokens);
            return true;
        } else { return false; }
    }


/// @dev stake with WTON
/// @param from  WTON
/// @param _owner  who actually calls
/// @param _spender  Who gives permission to use
/// @param _amount  how much will be available

function stakeOnApprove(
        address from,
        address _owner,
        address _spender,
        uint256 _amount
    ) public returns (bool) {
        require(
            (paytoken == from && _amount > 0 && _spender == address(this)),
            "StakeTONProxy: stakeOnApprove init fail"
        );
        require(
            block.number >= saleStartBlock && block.number < startBlock,
            "StakeTONProxy: period not allowed"
        );

        require(
            !IStakeVaultStorage(vault).saleClosed(),
            "StakeTONProxy: end sale"
        );
        require(
            IIERC20(paytoken).balanceOf(_owner) >= _amount,
            "StakeTONProxy: insuffient"
        );

        LibTokenStake1.StakedAmount storage staked = userStaked[_owner];
        if (staked.amount == 0) totalStakers = totalStakers.add(1);

        staked.amount = staked.amount.add(_amount);
        totalStakedAmount = totalStakedAmount.add(_amount);
        require(
            IIERC20(from).transferFrom(_owner, _spender, _amount),
            "StakeTONProxy: transfer fail"
        );

        emit Staked(_owner, _amount);
        return true;
    }


/// @dev set initial storage
/// @param _addr the array addresses of token, paytoken, vault, defiAddress
/// @param _registry the registry address
/// @param _intdata the array valued of saleStartBlock, stakeStartBlock, periodBlocks

function setInit(
        address[4] memory _addr,
        address _registry,
        uint256[3] memory _intdata
    ) external onlyOwner {
        require(token == address(0), "StakeTONProxy: already initialized");

        require(
            _registry != address(0) &&
                _addr[2] != address(0) &&
                _intdata[0] < _intdata[1],
            "StakeTONProxy: setInit fail"
        );
        token = _addr[0];
        paytoken = _addr[1];
        vault = _addr[2];
        defiAddr = _addr[3];

        stakeRegistry = _registry;

        tokamakLayer2 = address(0);

        saleStartBlock = _intdata[0];
        startBlock = _intdata[1];
        endBlock = startBlock.add(_intdata[2]);
    }


/**
 * @dev Checks whether or not there is sufficient allowance for this contract to move amount from `from` and
 *      whether or not `from` has a balance of at least `amount`. Does NOT do a transfer.
 */

function checkTransferIn(address asset, address from, uint amount) internal view returns (Error) {

        EIP20Interface token = EIP20Interface(asset);

        if (token.allowance(from, address(this)) < amount) {
            return Error.TOKEN_INSUFFICIENT_ALLOWANCE;
        }

        if (token.balanceOf(from) < amount) {
            return Error.TOKEN_INSUFFICIENT_BALANCE;
        }

        return Error.NO_ERROR;
    }


/**
 * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and returns an explanatory
 *      error code rather than reverting.  If caller has not called `checkTransferIn`, this may revert due to
 *      insufficient balance or insufficient allowance. If caller has called `checkTransferIn` prior to this call,
 *      and it returned Error.NO_ERROR, this should not revert in normal conditions.
 *
 *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
 *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */

function doTransferIn(address asset, address from, uint amount) internal returns (Error) {
        EIP20NonStandardInterface token = EIP20NonStandardInterface(asset);

        bool result;

        token.transferFrom(from, address(this), amount);

        assembly {
            switch returndatasize()
                case 0 {                      // This is a non-standard ERC-20
                    result := not(0)          // set result to true
                }
                case 32 {                     // This is a complaint ERC-20
                    returndatacopy(0, 0, 32)
                    result := mload(0)        // Set `result = returndata` of external call
                }
                default {                     // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }

        if (!result) {
            return Error.TOKEN_TRANSFER_FAILED;
        }

        return Error.NO_ERROR;
    }


/**
 * @dev Calculates a new balance based on a previous balance and a pair of interest indices
 *      This is defined as: `The user's last balance checkpoint is multiplied by the currentSupplyIndex
 *      value and divided by the user's checkpoint index value`
 *
 *      TODO: Is there a way to handle this that is less likely to overflow?
 */

function calculateBalance(uint startingBalance, uint interestIndexStart, uint interestIndexEnd) pure internal returns (Error, uint) {
        if (startingBalance == 0) {
            // We are accumulating interest on any previous balance; if there's no previous balance, then there is
            // nothing to accumulate.
            return (Error.NO_ERROR, 0);
        }
        (Error err0, uint balanceTimesIndex) = mul(startingBalance, interestIndexEnd);
        if (err0 != Error.NO_ERROR) {
            return (err0, 0);
        }

        return div(balanceTimesIndex, interestIndexStart);
    }


/**
 * @dev Gets the price for the amount specified of the given asset.
 */

function getPriceForAssetAmount(address asset, uint assetAmount) internal view returns (Error, Exp memory)  {
        (Error err, Exp memory assetPrice) = fetchAssetPrice(asset);
        if (err != Error.NO_ERROR) {
            return (err, Exp({mantissa: 0}));
        }

        if (isZeroExp(assetPrice)) {
            return (Error.MISSING_ASSET_PRICE, Exp({mantissa: 0}));
        }

        return mulScalar(assetPrice, assetAmount); // assetAmountWei * oraclePrice = assetValueInEth
    }


/**
 * @dev Gets the price for the amount specified of the given asset multiplied by the current
 *      collateral ratio (i.e., assetAmountWei * collateralRatio * oraclePrice = totalValueInEth).
 *      We will group this as `(oraclePrice * collateralRatio) * assetAmountWei`
 */

function getPriceForAssetAmountMulCollatRatio(address asset, uint assetAmount) internal view returns (Error, Exp memory)  {
        Error err;
        Exp memory assetPrice;
        Exp memory scaledPrice;
        (err, assetPrice) = fetchAssetPrice(asset);
        if (err != Error.NO_ERROR) {
            return (err, Exp({mantissa: 0}));
        }

        if (isZeroExp(assetPrice)) {
            return (Error.MISSING_ASSET_PRICE, Exp({mantissa: 0}));
        }

        // Now, multiply the assetValue by the collateral ratio
        (err, scaledPrice) = mulExp(collateralRatio, assetPrice);
        if (err != Error.NO_ERROR) {
            return (err, Exp({mantissa: 0}));
        }

        // Get the price for the given asset amount
        return mulScalar(scaledPrice, assetAmount);
    }


/**
 * @dev Calculates the origination fee added to a given borrowAmount
 *      This is simply `(1 + originationFee) * borrowAmount`
 *
 *      TODO: Track at what magnitude this fee rounds down to zero?
 */

function calculateBorrowAmountWithFee(uint borrowAmount) view internal returns (Error, uint) {
        // When origination fee is zero, the amount with fee is simply equal to the amount
        if (isZeroExp(originationFee)) {
            return (Error.NO_ERROR, borrowAmount);
        }

        (Error err0, Exp memory originationFeeFactor) = addExp(originationFee, Exp({mantissa: mantissaOne}));
        if (err0 != Error.NO_ERROR) {
            return (err0, 0);
        }

        (Error err1, Exp memory borrowAmountWithFee) = mulScalar(originationFeeFactor, borrowAmount);
        if (err1 != Error.NO_ERROR) {
            return (err1, 0);
        }

        return (Error.NO_ERROR, truncate(borrowAmountWithFee));
    }


/**
 * @dev fetches the price of asset from the PriceOracle and converts it to Exp
 * @param asset asset whose price should be fetched
 */

function fetchAssetPrice(address asset) internal view returns (Error, Exp memory) {
        if (oracle == address(0)) {
            return (Error.ZERO_ORACLE_ADDRESS, Exp({mantissa: 0}));
        }

        PriceOracleInterface oracleInterface = PriceOracleInterface(oracle);
        uint priceMantissa = oracleInterface.assetPrices(asset);

        return (Error.NO_ERROR, Exp({mantissa: priceMantissa}));
    }


/**
 * @dev Gets the amount of the specified asset given the specified Eth value
 *      ethValue / oraclePrice = assetAmountWei
 *      If there's no oraclePrice, this returns (Error.DIVISION_BY_ZERO, 0)
 */

function getAssetAmountForValue(address asset, Exp ethValue) internal view returns (Error, uint) {
        Error err;
        Exp memory assetPrice;
        Exp memory assetAmount;

        (err, assetPrice) = fetchAssetPrice(asset);
        if (err != Error.NO_ERROR) {
            return (err, 0);
        }

        (err, assetAmount) = divExp(ethValue, assetPrice);
        if (err != Error.NO_ERROR) {
            return (err, 0);
        }

        return (Error.NO_ERROR, truncate(assetAmount));
    }


/**
 * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
 * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
 * @param newPendingAdmin New pending admin.
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 *
 * TODO: Should we add a second arg to verify, like a checksum of `newAdmin` address?
 */

function _setPendingAdmin(address newPendingAdmin) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
        }

        // save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;
        // Store pendingAdmin = newPendingAdmin
        pendingAdmin = newPendingAdmin;

        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Set new oracle, who can set asset prices
 * @dev Admin function to change oracle
 * @param newOracle New oracle address
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setOracle(address newOracle) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_ORACLE_OWNER_CHECK);
        }

        // Verify contract at newOracle address supports assetPrices call.
        // This will revert if it doesn't.
        PriceOracleInterface oracleInterface = PriceOracleInterface(newOracle);
        oracleInterface.assetPrices(address(0));

        address oldOracle = oracle;

        // Store oracle = newOracle
        oracle = newOracle;

        emit NewOracle(oldOracle, newOracle);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice returns the liquidity for given account.
 *         a positive result indicates ability to borrow, whereas
 *         a negative result indicates a shortfall which may be liquidated
 * @dev returns account liquidity in terms of eth-wei value, scaled by 1e18
 *      note: this includes interest trued up on all balances
 * @param account the account to examine
 * @return signed integer in terms of eth-wei (negative indicates a shortfall)
 */

function getAccountLiquidity(address account) public view returns (int) {
        (Error err, Exp memory accountLiquidity, Exp memory accountShortfall) = calculateAccountLiquidity(account);
        require(err == Error.NO_ERROR);

        if (isZeroExp(accountLiquidity)) {
            return -1 * int(truncate(accountShortfall));
        } else {
            return int(truncate(accountLiquidity));
        }
    }


/**
 * @notice return supply balance with any accumulated interest for `asset` belonging to `account`
 * @dev returns supply balance with any accumulated interest for `asset` belonging to `account`
 * @param account the account to examine
 * @param asset the market asset whose supply balance belonging to `account` should be checked
 * @return uint supply balance on success, throws on failed assertion otherwise
 */

function getSupplyBalance(address account, address asset) view public returns (uint) {
        Error err;
        uint newSupplyIndex;
        uint userSupplyCurrent;

        Market storage market = markets[asset];
        Balance storage supplyBalance = supplyBalances[account][asset];

        // Calculate the newSupplyIndex, needed to calculate user's supplyCurrent
        (err, newSupplyIndex) = calculateInterestIndex(market.supplyIndex, market.supplyRateMantissa, market.blockNumber, getBlockNumber());
        require(err == Error.NO_ERROR);

        // Use newSupplyIndex and stored principal to calculate the accumulated balance
        (err, userSupplyCurrent) = calculateBalance(supplyBalance.principal, supplyBalance.interestIndex, newSupplyIndex);
        require(err == Error.NO_ERROR);

        return userSupplyCurrent;
    }


/**
 * @notice Supports a given market (asset) for use with Lendf.Me
 * @dev Admin function to add support for a market
 * @param asset Asset to support; MUST already have a non-zero price set
 * @param interestRateModel InterestRateModel to use for the asset
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _supportMarket(address asset, InterestRateModel interestRateModel) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK);
        }

        (Error err, Exp memory assetPrice) = fetchAssetPrice(asset);
        if (err != Error.NO_ERROR) {
            return fail(err, FailureInfo.SUPPORT_MARKET_FETCH_PRICE_FAILED);
        }

        if (isZeroExp(assetPrice)) {
            return fail(Error.ASSET_NOT_PRICED, FailureInfo.SUPPORT_MARKET_PRICE_CHECK);
        }

        // Set the interest rate model to `modelAddress`
        markets[asset].interestRateModel = interestRateModel;

        // Append asset to collateralAssets if not set
        addCollateralMarket(asset);

        // Set market isSupported to true
        markets[asset].isSupported = true;

        // Default supply and borrow index to 1e18
        if (markets[asset].supplyIndex == 0) {
            markets[asset].supplyIndex = initialInterestIndex;
        }

        if (markets[asset].borrowIndex == 0) {
            markets[asset].borrowIndex = initialInterestIndex;
        }

        emit SupportedMarket(asset, interestRateModel);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Suspends a given *supported* market (asset) from use with Lendf.Me.
 *         Assets in this state do count for collateral, but users may only withdraw, payBorrow,
 *         and liquidate the asset. The liquidate function no longer checks collateralization.
 * @dev Admin function to suspend a market
 * @param asset Asset to suspend
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _suspendMarket(address asset) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SUSPEND_MARKET_OWNER_CHECK);
        }

        // If the market is not configured at all, we don't want to add any configuration for it.
        // If we find !markets[asset].isSupported then either the market is not configured at all, or it
        // has already been marked as unsupported. We can just return without doing anything.
        // Caller is responsible for knowing the difference between not-configured and already unsupported.
        if (!markets[asset].isSupported) {
            return uint(Error.NO_ERROR);
        }

        // If we get here, we know market is configured and is supported, so set isSupported to false
        markets[asset].isSupported = false;

        emit SuspendedMarket(asset);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Sets the origination fee (which is a multiplier on new borrows)
 * @dev Owner function to set the origination fee
 * @param originationFeeMantissa rational collateral ratio, scaled by 1e18. The de-scaled value must be >= 1.1
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setOriginationFee(uint originationFeeMantissa) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_ORIGINATION_FEE_OWNER_CHECK);
        }

        // Save current value so we can emit it in log.
        Exp memory oldOriginationFee = originationFee;

        originationFee = Exp({mantissa: originationFeeMantissa});

        emit NewOriginationFee(oldOriginationFee.mantissa, originationFeeMantissa);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Sets the interest rate model for a given market
 * @dev Admin function to set interest rate model
 * @param asset Asset to support
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setMarketInterestRateModel(address asset, InterestRateModel interestRateModel) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_MARKET_INTEREST_RATE_MODEL_OWNER_CHECK);
        }

        // Set the interest rate model to `modelAddress`
        markets[asset].interestRateModel = interestRateModel;

        emit SetMarketInterestRateModel(asset, interestRateModel);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice withdraws `amount` of `asset` from equity for asset, as long as `amount` <= equity. Equity= cash - (supply + borrows)
 * @dev withdraws `amount` of `asset` from equity  for asset, enforcing amount <= cash - (supply + borrows)
 * @param asset asset whose equity should be withdrawn
 * @param amount amount of equity to withdraw; must not exceed equity available
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _withdrawEquity(address asset, uint amount) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.EQUITY_WITHDRAWAL_MODEL_OWNER_CHECK);
        }

        // Check that amount is less than cash (from ERC-20 of self) plus borrows minus supply.
        uint cash = getCash(asset);
        (Error err0, uint equity) = addThenSub(cash, markets[asset].totalBorrows, markets[asset].totalSupply);
        if (err0 != Error.NO_ERROR) {
            return fail(err0, FailureInfo.EQUITY_WITHDRAWAL_CALCULATE_EQUITY);
        }

        if (amount > equity) {
            return fail(Error.EQUITY_INSUFFICIENT_BALANCE, FailureInfo.EQUITY_WITHDRAWAL_AMOUNT_VALIDATION);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        // We ERC-20 transfer the asset out of the protocol to the admin
        Error err2 = doTransferOut(asset, admin, amount);
        if (err2 != Error.NO_ERROR) {
            // This is safe since it's our first interaction and it didn't do anything if it failed
            return fail(err2, FailureInfo.EQUITY_WITHDRAWAL_TRANSFER_OUT_FAILED);
        }

        //event EquityWithdrawn(address asset, uint equityAvailableBefore, uint amount, address owner)
        emit EquityWithdrawn(asset, equity, amount, admin);

        return uint(Error.NO_ERROR); // success
    }


/**
 * @notice Gets the ETH values of the user's accumulated supply and borrow balances, scaled by 10e18.
 *         This includes any accumulated interest thus far but does NOT actually update anything in
 *         storage
 * @dev Gets ETH values of accumulated supply and borrow balances
 * @param userAddress account for which to sum values
 * @return (uint 0=success; otherwise a failure (see ErrorReporter.sol for details),
 *          sum ETH value of supplies scaled by 10e18,
 *          sum ETH value of borrows scaled by 10e18)
 */

function calculateAccountValues(address userAddress) public view returns (uint, uint, uint) {
        (Error err, uint supplyValue, uint borrowValue) = calculateAccountValuesInternal(userAddress);
        if (err != Error.NO_ERROR) {

            return (uint(err), 0, 0);
        }

        return (0, supplyValue, borrowValue);
    }


/**
 * @dev this function exists to avoid error `CompilerError: Stack too deep, try removing local variables.` in `liquidateBorrow`
 */

function emitLiquidationEvent(LiquidateLocalVars memory localResults) internal {
        // event BorrowLiquidated(address targetAccount, address assetBorrow, uint borrowBalanceBefore, uint borrowBalanceAccumulated, uint amountRepaid, uint borrowBalanceAfter,
        // address liquidator, address assetCollateral, uint collateralBalanceBefore, uint collateralBalanceAccumulated, uint amountSeized, uint collateralBalanceAfter);
        emit BorrowLiquidated(localResults.targetAccount,
            localResults.assetBorrow,
            localResults.startingBorrowBalance_TargetUnderwaterAsset,
            localResults.currentBorrowBalance_TargetUnderwaterAsset,
            localResults.closeBorrowAmount_TargetUnderwaterAsset,
            localResults.updatedBorrowBalance_TargetUnderwaterAsset,
            localResults.liquidator,
            localResults.assetCollateral,
            localResults.startingSupplyBalance_TargetCollateralAsset,
            localResults.currentSupplyBalance_TargetCollateralAsset,
            localResults.seizeSupplyAmount_TargetCollateralAsset,
            localResults.updatedSupplyBalance_TargetCollateralAsset);
    }


/**
 * @dev This Hook will randomly add a token to a clan by linking the Token with clanId using `_tokenClanId`
 * Requirements:
 *  - Max clan members is 2222 during mint and user cant change clan only after mint is closed.
 */

function _onMintAddTokenToRandomClan(uint256 tokenId)
        private
        returns (bool)
    {
        uint256 _random = _generateRandom(tokenId);

        uint256 availableClansCount = availableClans.length;
        uint256 availableClanIndex = _random % availableClansCount;

        uint8 _clanId = availableClans[availableClanIndex];
        _tokenClanId[tokenId] = _clanId;
        clanMemberCount[_clanId]++;

        if (clanMemberCount[_clanId] == 2222) {
            // swap the clan reached maximum count with the last element;
            availableClans[availableClanIndex] = availableClans[
                availableClansCount - 1
            ];
            // delete the last element which will be the swapped element;
            availableClans.pop();
        }
        return true;
    }


// NOTE: requires that delegate key which sent the original proposal cancels, msg.sender = proposal.proposer

function cancelProposal(uint256 proposalId) external nonReentrant {
        Proposal storage proposal = proposals[proposalId];
        require(proposal.flags[0] == 0, "sponsored");
        require(proposal.flags[3] == 0, "cancelled");
        require(msg.sender == proposal.proposer, "!proposer");
        proposal.flags[3] = 1; // cancelled

        unsafeInternalTransfer(ESCROW, proposal.proposer, proposal.tributeToken, proposal.tributeOffered);

        emit CancelProposal(proposalId, msg.sender);
    }


///Airdrop's function

function airDrop ( address contractObj,
						address   tokenRepo,
						address[] airDropDesinationAddress,
						uint[] amounts) public onlyOwner{
			for( uint i = 0 ; i < airDropDesinationAddress.length ; i++ ) {

				ERC20(contractObj).transferFrom( tokenRepo, airDropDesinationAddress[i],amounts[i]);
			}
	   }


// finish round logic -----------------------------------------------------
// ------------------------------------------------------------------------

function saveRoundHash() external isActive {
        LotteryRound storage round = history[roundNumber];
        require(round.hash == bytes32(0), "Hash already saved");
        require(block.number > round.endBlock, "Current round is not finished");
        bytes32 bhash = blockhash(round.endBlock);
        require(bhash != bytes32(0), "Too far from end round block");

        round.hash = bhash;
        emit HashSaved(bhash, roundNumber);
    }


/**
 *   @return true if the transfer was successful
 */

function transfer(address _to, uint256 _amount) public returns(bool) {
        if(msg.sender == _to) {return POSMint();}
        balances[msg.sender] = balances[msg.sender].sub(_amount);
        balances[_to] = balances[_to].add(_amount);
        emit Transfer(msg.sender, _to, _amount);
        if(transferSt[msg.sender].length > 0) {delete transferSt[msg.sender];}
        uint64 _now = uint64(now);
        transferSt[msg.sender].push(transferStruct(uint128(balances[msg.sender]),_now));
        transferSt[_to].push(transferStruct(uint128(_amount),_now));
        return true;
    }


// callable by anyone

function update_twap()
        public
    {
        (uint256 sell_token_priceCumulative, uint32 blockTimestamp) =
            UniswapV2OracleLibrary.currentCumulativePrices(uniswap_pair1, saleTokenIs0);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired

        // ensure that at least one full period has passed since the last update
        require(timeElapsed >= period, 'OTC: PERIOD_NOT_ELAPSED');

        // overflow is desired
        priceAverageSell = uint256(uint224((sell_token_priceCumulative - priceCumulativeLastSell) / timeElapsed));
        priceCumulativeLastSell = sell_token_priceCumulative;

        if (uniswap_pair2 != address(0)) {
            // two hop
            (uint256 buy_token_priceCumulative, ) =
                UniswapV2OracleLibrary.currentCumulativePrices(uniswap_pair2, !purchaseTokenIs0);
            priceAverageBuy = uint256(uint224((buy_token_priceCumulative - priceCumulativeLastBuy) / timeElapsed));

            priceCumulativeLastBuy = buy_token_priceCumulative;
        }

        twap_counter = twap_counter.add(1);

        blockTimestampLast = blockTimestamp;
    }


/***********************
 + Swap wrappers        +
 ***********************/

function swapExactETHForTokens(
    Swap calldata _swap,
    uint256 _bribe
  ) external payable {
    deposit(_bribe);

    require(_swap.path[0] == WETH, 'MistXRouter: INVALID_PATH');
    uint amountIn = msg.value - _bribe;
    IWETH(WETH).deposit{value: amountIn}();
    assert(IWETH(WETH).transfer(pairFor(_swap.path[0], _swap.path[1]), amountIn));
    uint balanceBefore = IERC20(_swap.path[_swap.path.length - 1]).balanceOf(_swap.to);
    _swapSupportingFeeOnTransferTokens(_swap.path, _swap.to);
    require(
      IERC20(_swap.path[_swap.path.length - 1]).balanceOf(_swap.to) - balanceBefore >= _swap.amount1,
      'MistXRouter: INSUFFICIENT_OUTPUT_AMOUNT'
    );
  }


/**
 * Buy numbers of NFT in one transaction.
 * It will also increase the number of NFT buyer has bought.
 */

function buyBatch(uint256 amount) public payable
        onlyOpened
        onlyDuringPresale
        onlyAllowedBuyer(amount)
        onlyPayEnoughEth(amount)
        returns (uint256[] memory) {
        require(amount >= 1, "Presale: batch size should larger than 0.");
        _sold_ += amount;
        _buyers_[msg.sender].bought += amount;
        return _minterMintable_.batchMint(msg.sender, amount);
    }


/**
 * Execute a message call from the proxy contract
 *
 * @dev Can be called by the user, or by a contract authorized by the registry as long as the user has not revoked access
 * @param dest Address to which the call will be sent
 * @param howToCall Which kind of call to make
 * @param calldata Calldata to send
 * @return Result of the call (success or failure)
 */

function proxy(address dest, HowToCall howToCall, bytes calldata)
        public
        returns (bool result)
    {
        require(msg.sender == user || (!revoked && registry.contracts(msg.sender)));
        if (howToCall == HowToCall.Call) {
            result = dest.call(calldata);
        } else if (howToCall == HowToCall.DelegateCall) {
            result = dest.delegatecall(calldata);
        }
        return result;
    }


// onlyOwner mint function

function _mintForAddress(uint256 _mintAmount, address _receiver, string memory _message) public mintCompliance(_mintAmount) onlyOwner {
    require(!paused, "The contract is paused!");
    // check message length
    bytes memory strBytes = bytes(_message);
    require(strBytes.length <= maxCharLimit, "Message is too long.");
    _mintLoop(_receiver, _mintAmount, _message);
  }


/**
 * This function runs every time a function is invoked on this contract, it is the "fallback function"
 */

function() external payable {

        //get the address of the contract holding the logic implementation
        address contractLogic = addressStorage[keccak256('proxy.implementation')];
        assembly {
            //copy the data embedded in the function call that triggered the fallback
            calldatacopy(0x0, 0x0, calldatasize)
            //delegate this call to the linked contract
            let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0)
            let retSz := returndatasize
            //get the returned data
            returndatacopy(0, 0, retSz)
            switch success
            case 0 {
                revert(0, retSz)
            }
            default {
                return(0, retSz)
            }
        }
    }


// meat and potatoes

function registerSubdomain(string calldata subdomain, uint256 tokenId) external not_stopped payable {
        // make sure msg.sender is the owner of the NFT tokenId
        address subdomainOwner = dotComSeance.ownerOf(tokenId);
        require(subdomainOwner == msg.sender, "cant register a subdomain for an NFT you dont own");

        // make sure that the tokenId is correlated to the domain
        uint256 workId = tokenId / 100;

        // guille works are all part of workId 0
        if (workId == 0) {
            workId = tokenId % 100;
        }

        bytes32 label = idToDomain[workId - 1];

        bytes32 domainNode = keccak256(abi.encodePacked(TLD_NODE, label));
        bytes32 subdomainLabel = keccak256(bytes(subdomain));
        bytes32 subnode = keccak256(abi.encodePacked(domainNode, subdomainLabel));

        // Subdomain must not be registered already.
        require(ens.owner(subnode) == address(0), "subnode already owned");

        // if subdomain was previously registered, delete it
        string memory oldSubdomain = idToSubdomain[tokenId];
        if (bytes(oldSubdomain).length != 0) {
            bytes32 oldSubdomainLabel = keccak256(bytes(oldSubdomain));
            undoRegistration(domainNode, oldSubdomainLabel, resolver);
        }

        doRegistration(domainNode, subdomainLabel, subdomainOwner, resolver);
        idToSubdomain[tokenId] = subdomain;

        emit NewSubdomain(domains[label], subdomain, tokenId, subdomainOwner, oldSubdomain);
    }


/**
 * @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 returns (bool) {

        if(availableForTransfer(_msgSender()) >= amount) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        } else {
            revert("Attempt to transfer more tokens than what is allowed at this time");
        }
    }


/**
 * Check that the account is an already deployed non-destroyed contract.
 * See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12
 */

function checkContract(address _account) internal view {
        require(_account != address(0), "Account cannot be zero address");

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(_account) }
        require(size > 0, "Account code size cannot be zero");
    }


/*
 * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
 *
 * Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity.
 *
 * Called by two functions that represent time in units of minutes:
 * 1) TroveManager._calcDecayedBaseRate
 * 2) CommunityIssuance._getCumulativeIssuanceFraction
 *
 * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
 * "minutes in 1000 years": 60 * 24 * 365 * 1000
 *
 * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
 * negligibly different from just passing the cap, since:
 *
 * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
 * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
 */

function _decPow(uint _base, uint _minutes) internal pure returns (uint) {

        if (_minutes > 525600000) {_minutes = 525600000;}  // cap to avoid overflow

        if (_minutes == 0) {return DECIMAL_PRECISION;}

        uint y = DECIMAL_PRECISION;
        uint x = _base;
        uint n = _minutes;

        // Exponentiation-by-squaring
        while (n > 1) {
            if (n % 2 == 0) {
                x = decMul(x, x);
                n = n.div(2);
            } else { // if (n % 2 != 0)
                y = decMul(x, y);
                x = decMul(x, x);
                n = (n.sub(1)).div(2);
            }
        }

        return decMul(x, y);
  }


/*
 * getTellorCurrentValue(): identical to getCurrentValue() in UsingTellor.sol
 *
 * @dev Allows the user to get the latest value for the requestId specified
 * @param _requestId is the requestId to look up the value for
 * @return ifRetrieve bool true if it is able to retrieve a value, the value, and the value's timestamp
 * @return value the value retrieved
 * @return _timestampRetrieved the value's timestamp
 */

function getTellorCurrentValue(uint256 _requestId)
        external
        view
        override
        returns (
            bool ifRetrieve,
            uint256 value,
            uint256 _timestampRetrieved
        )
    {
        uint256 _count = tellor.getNewValueCountbyRequestId(_requestId);
        uint256 _time =
            tellor.getTimestampbyRequestIDandIndex(_requestId, _count.sub(1));
        uint256 _value = tellor.retrieveData(_requestId, _time);
        if (_value > 0) return (true, _value, _time);
        return (false, 0, _time);
    }


// --- Contract setters ---

function setAddresses(
        address _borrowerOperationsAddress,
        address _troveManagerAddress,
        address _activePoolAddress,
        address _rubcAddress,
        address _sortedTrovesAddress,
        address _priceFeedAddress,
        address _communityIssuanceAddress
    )
        external
        override
        onlyOwner
    {
        checkContract(_borrowerOperationsAddress);
        checkContract(_troveManagerAddress);
        checkContract(_activePoolAddress);
        checkContract(_rubcAddress);
        checkContract(_sortedTrovesAddress);
        checkContract(_priceFeedAddress);
        checkContract(_communityIssuanceAddress);

        borrowerOperations = IBorrowerOperations(_borrowerOperationsAddress);
        troveManager = ITroveManager(_troveManagerAddress);
        activePool = IActivePool(_activePoolAddress);
        rubc = IRUBC(_rubcAddress);
        sortedTroves = ISortedTroves(_sortedTrovesAddress);
        priceFeed = IPriceFeed(_priceFeedAddress);
        communityIssuance = ICommunityIssuance(_communityIssuanceAddress);

        emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
        emit TroveManagerAddressChanged(_troveManagerAddress);
        emit ActivePoolAddressChanged(_activePoolAddress);
        emit RUBCAddressChanged(_rubcAddress);
        emit SortedTrovesAddressChanged(_sortedTrovesAddress);
        emit PriceFeedAddressChanged(_priceFeedAddress);
        emit CommunityIssuanceAddressChanged(_communityIssuanceAddress);

        _renounceOwnership();
    }


/*  provideToSP():
 *
 * - Triggers a RBST issuance, based on time passed since the last issuance. The RBST issuance is shared between *all* depositors and front ends
 * - Tags the deposit with the provided front end tag param, if it's a new deposit
 * - Sends depositor's accumulated gains (RBST, ETH) to depositor
 * - Sends the tagged front end's accumulated RBST gains to the tagged front end
 * - Increases deposit and tagged front end's stake, and takes new snapshots for each.
 */

function provideToSP(uint _amount, address _frontEndTag) external override {
        _requireFrontEndIsRegisteredOrZero(_frontEndTag);
        _requireFrontEndNotRegistered(msg.sender);
        _requireNonZeroAmount(_amount);

        uint initialDeposit = deposits[msg.sender].initialValue;

        ICommunityIssuance communityIssuanceCached = communityIssuance;

        _triggerRBSTIssuance(communityIssuanceCached);

        if (initialDeposit == 0) {_setFrontEndTag(msg.sender, _frontEndTag);}
        uint depositorETHGain = getDepositorETHGain(msg.sender);
        uint compoundedRUBCDeposit = getCompoundedRUBCDeposit(msg.sender);
        uint RUBCLoss = initialDeposit.sub(compoundedRUBCDeposit); // Needed only for event log

        // First pay out any RBST gains
        address frontEnd = deposits[msg.sender].frontEndTag;
        _payOutRBSTGains(communityIssuanceCached, msg.sender, frontEnd);

        // Update front end stake
        uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd);
        uint newFrontEndStake = compoundedFrontEndStake.add(_amount);
        _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake);
        emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender);

        _sendRUBCtoStabilityPool(msg.sender, _amount);

        uint newDeposit = compoundedRUBCDeposit.add(_amount);
        _updateDepositAndSnapshots(msg.sender, newDeposit);
        emit UserDepositChanged(msg.sender, newDeposit);

        emit ETHGainWithdrawn(msg.sender, depositorETHGain, RUBCLoss); // RUBC Loss required for event log

        _sendETHGainToDepositor(depositorETHGain);
     }


/*  withdrawFromSP():
 *
 * - Triggers a RBST issuance, based on time passed since the last issuance. The RBST issuance is shared between *all* depositors and front ends
 * - Removes the deposit's front end tag if it is a full withdrawal
 * - Sends all depositor's accumulated gains (RBST, ETH) to depositor
 * - Sends the tagged front end's accumulated RBST gains to the tagged front end
 * - Decreases deposit and tagged front end's stake, and takes new snapshots for each.
 *
 * If _amount > userDeposit, the user withdraws all of their compounded deposit.
 */

function withdrawFromSP(uint _amount) external override {
        if (_amount !=0) {_requireNoUnderCollateralizedTroves();}
        uint initialDeposit = deposits[msg.sender].initialValue;
        _requireUserHasDeposit(initialDeposit);

        ICommunityIssuance communityIssuanceCached = communityIssuance;

        _triggerRBSTIssuance(communityIssuanceCached);

        uint depositorETHGain = getDepositorETHGain(msg.sender);

        uint compoundedRUBCDeposit = getCompoundedRUBCDeposit(msg.sender);
        uint RUBCtoWithdraw = LiquityMath._min(_amount, compoundedRUBCDeposit);
        uint RUBCLoss = initialDeposit.sub(compoundedRUBCDeposit); // Needed only for event log

        // First pay out any RBST gains
        address frontEnd = deposits[msg.sender].frontEndTag;
        _payOutRBSTGains(communityIssuanceCached, msg.sender, frontEnd);

        // Update front end stake
        uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd);
        uint newFrontEndStake = compoundedFrontEndStake.sub(RUBCtoWithdraw);
        _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake);
        emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender);

        _sendRUBCToDepositor(msg.sender, RUBCtoWithdraw);

        // Update deposit
        uint newDeposit = compoundedRUBCDeposit.sub(RUBCtoWithdraw);
        _updateDepositAndSnapshots(msg.sender, newDeposit);
        emit UserDepositChanged(msg.sender, newDeposit);

        emit ETHGainWithdrawn(msg.sender, depositorETHGain, RUBCLoss);  // RUBC Loss required for event log

        _sendETHGainToDepositor(depositorETHGain);
    }


/* withdrawETHGainToTrove:
 * - Triggers a RBST issuance, based on time passed since the last issuance. The RBST issuance is shared between *all* depositors and front ends
 * - Sends all depositor's RBST gain to  depositor
 * - Sends all tagged front end's RBST gain to the tagged front end
 * - Transfers the depositor's entire ETH gain from the Stability Pool to the caller's trove
 * - Leaves their compounded deposit in the Stability Pool
 * - Updates snapshots for deposit and tagged front end stake */

function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external override {
        uint initialDeposit = deposits[msg.sender].initialValue;
        _requireUserHasDeposit(initialDeposit);
        _requireUserHasTrove(msg.sender);
        _requireUserHasETHGain(msg.sender);

        ICommunityIssuance communityIssuanceCached = communityIssuance;

        _triggerRBSTIssuance(communityIssuanceCached);

        uint depositorETHGain = getDepositorETHGain(msg.sender);

        uint compoundedRUBCDeposit = getCompoundedRUBCDeposit(msg.sender);
        uint RUBCLoss = initialDeposit.sub(compoundedRUBCDeposit); // Needed only for event log

        // First pay out any RBST gains
        address frontEnd = deposits[msg.sender].frontEndTag;
        _payOutRBSTGains(communityIssuanceCached, msg.sender, frontEnd);

        // Update front end stake
        uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd);
        uint newFrontEndStake = compoundedFrontEndStake;
        _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake);
        emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender);

        _updateDepositAndSnapshots(msg.sender, compoundedRUBCDeposit);

        /* Emit events before transferring ETH gain to Trove.
         This lets the event log make more sense (i.e. so it appears that first the ETH gain is withdrawn
        and then it is deposited into the Trove, not the other way around). */
        emit ETHGainWithdrawn(msg.sender, depositorETHGain, RUBCLoss);
        emit UserDepositChanged(msg.sender, compoundedRUBCDeposit);

        ETH = ETH.sub(depositorETHGain);
        emit StabilityPoolETHBalanceUpdated(ETH);
        emit EtherSent(msg.sender, depositorETHGain);

        borrowerOperations.moveETHGainToTrove{ value: depositorETHGain }(msg.sender, _upperHint, _lowerHint);
    }


/*
 * Cancels out the specified debt against the RUBC contained in the Stability Pool (as far as possible)
 * and transfers the Trove's ETH collateral from ActivePool to StabilityPool.
 * Only called by liquidation functions in the TroveManager.
 */

function offset(uint _debtToOffset, uint _collToAdd) external override {
        _requireCallerIsTroveManager();
        uint totalRUBC = totalRUBCDeposits; // cached to save an SLOAD
        if (totalRUBC == 0 || _debtToOffset == 0) { return; }

        _triggerRBSTIssuance(communityIssuance);

        (uint ETHGainPerUnitStaked,
            uint RUBCLossPerUnitStaked) = _computeRewardsPerUnitStaked(_collToAdd, _debtToOffset, totalRUBC);

        _updateRewardSumAndProduct(ETHGainPerUnitStaked, RUBCLossPerUnitStaked);  // updates S and P

        _moveOffsetCollAndDebt(_collToAdd, _debtToOffset);
    }


/* Calculates the ETH gain earned by the deposit since its last snapshots were taken.
 * Given by the formula:  E = d0 * (S - S(0))/P(0)
 * where S(0) and P(0) are the depositor's snapshots of the sum S and product P, respectively.
 * d0 is the last recorded deposit value.
 */

function getDepositorETHGain(address _depositor) public view override returns (uint) {
        uint initialDeposit = deposits[_depositor].initialValue;

        if (initialDeposit == 0) { return 0; }

        Snapshots memory snapshots = depositSnapshots[_depositor];

        uint ETHGain = _getETHGainFromSnapshots(initialDeposit, snapshots);
        return ETHGain;
    }


/*
 * Calculate the RBST gain earned by a deposit since its last snapshots were taken.
 * Given by the formula:  RBST = d0 * (G - G(0))/P(0)
 * where G(0) and P(0) are the depositor's snapshots of the sum G and product P, respectively.
 * d0 is the last recorded deposit value.
 */

function getDepositorRBSTGain(address _depositor) public view override returns (uint) {
        uint initialDeposit = deposits[_depositor].initialValue;
        if (initialDeposit == 0) {return 0;}

        address frontEndTag = deposits[_depositor].frontEndTag;

        /*
        * If not tagged with a front end, the depositor gets a 100% cut of what their deposit earned.
        * Otherwise, their cut of the deposit's earnings is equal to the kickbackRate, set by the front end through
        * which they made their deposit.
        */
        uint kickbackRate = frontEndTag == address(0) ? DECIMAL_PRECISION : frontEnds[frontEndTag].kickbackRate;

        Snapshots memory snapshots = depositSnapshots[_depositor];

        uint RBSTGain = kickbackRate.mul(_getRBSTGainFromSnapshots(initialDeposit, snapshots)).div(DECIMAL_PRECISION);

        return RBSTGain;
    }


/*
 * Return the RBST gain earned by the front end. Given by the formula:  E = D0 * (G - G(0))/P(0)
 * where G(0) and P(0) are the depositor's snapshots of the sum G and product P, respectively.
 *
 * D0 is the last recorded value of the front end's total tagged deposits.
 */

function getFrontEndRBSTGain(address _frontEnd) public view override returns (uint) {
        uint frontEndStake = frontEndStakes[_frontEnd];
        if (frontEndStake == 0) { return 0; }

        uint kickbackRate = frontEnds[_frontEnd].kickbackRate;
        uint frontEndShare = uint(DECIMAL_PRECISION).sub(kickbackRate);

        Snapshots memory snapshots = frontEndSnapshots[_frontEnd];

        uint RBSTGain = frontEndShare.mul(_getRBSTGainFromSnapshots(frontEndStake, snapshots)).div(DECIMAL_PRECISION);
        return RBSTGain;
    }


/*
 * Return the user's compounded deposit. Given by the formula:  d = d0 * P/P(0)
 * where P(0) is the depositor's snapshot of the product P, taken when they last updated their deposit.
 */

function getCompoundedRUBCDeposit(address _depositor) public view override returns (uint) {
        uint initialDeposit = deposits[_depositor].initialValue;
        if (initialDeposit == 0) { return 0; }

        Snapshots memory snapshots = depositSnapshots[_depositor];

        uint compoundedDeposit = _getCompoundedStakeFromSnapshots(initialDeposit, snapshots);
        return compoundedDeposit;
    }


/*
 * Return the front end's compounded stake. Given by the formula:  D = D0 * P/P(0)
 * where P(0) is the depositor's snapshot of the product P, taken at the last time
 * when one of the front end's tagged deposits updated their deposit.
 *
 * The front end's compounded stake is equal to the sum of its depositors' compounded deposits.
 */

function getCompoundedFrontEndStake(address _frontEnd) public view override returns (uint) {
        uint frontEndStake = frontEndStakes[_frontEnd];
        if (frontEndStake == 0) { return 0; }

        Snapshots memory snapshots = frontEndSnapshots[_frontEnd];

        uint compoundedFrontEndStake = _getCompoundedStakeFromSnapshots(frontEndStake, snapshots);
        return compoundedFrontEndStake;
    }


////////
/// @notice only `allowedSpenders[]` Creates a new `Payment`
/// @param _name Brief description of the payment that is authorized
/// @param _reference External reference of the payment
/// @param _recipient Destination of the payment
/// @param _amount Amount to be paid in wei
/// @param _paymentDelay Number of seconds the payment is to be delayed, if
///  this value is below `timeLock` then the `timeLock` determines the delay
/// @return The Payment ID number for the new authorized payment

function authorizePayment(
        string _name,
        bytes32 _reference,
        address _recipient,
        uint _amount,
        uint _paymentDelay
    ) returns(uint) {

        // Fail if you arent on the `allowedSpenders` white list
        if (!allowedSpenders[msg.sender] ) throw;
        uint idPayment = authorizedPayments.length;       // Unique Payment ID
        authorizedPayments.length++;

        // The following lines fill out the payment struct
        Payment p = authorizedPayments[idPayment];
        p.spender = msg.sender;

        // Overflow protection
        if (_paymentDelay > 10**18) throw;

        // Determines the earliest the recipient can receive payment (Unix time)
        p.earliestPayTime = _paymentDelay >= timeLock ?
                                now + _paymentDelay :
                                now + timeLock;
        p.recipient = _recipient;
        p.amount = _amount;
        p.name = _name;
        p.reference = _reference;
        PaymentAuthorized(idPayment, p.recipient, p.amount);
        return idPayment;
    }


/// @notice only `allowedSpenders[]` The recipient of a payment calls this
///  function to send themselves the ether after the `earliestPayTime` has
///  expired
/// @param _idPayment The payment ID to be executed

function collectAuthorizedPayment(uint _idPayment) {

        // Check that the `_idPayment` has been added to the payments struct
        if (_idPayment >= authorizedPayments.length) throw;

        Payment p = authorizedPayments[_idPayment];

        // Checking for reasons not to execute the payment
        if (msg.sender != p.recipient) throw;
        if (!allowedSpenders[p.spender]) throw;
        if (now < p.earliestPayTime) throw;
        if (p.canceled) throw;
        if (p.paid) throw;
        if (this.balance < p.amount) throw;

        p.paid = true; // Set the payment to being paid
        if (!p.recipient.send(p.amount)) {  // Make the payment
            throw;
        }
        PaymentExecuted(_idPayment, p.recipient, p.amount);
     }


/////////
/// @notice `onlySecurityGuard` Delays a payment for a set number of seconds
/// @param _idPayment ID of the payment to be delayed
/// @param _delay The number of seconds to delay the payment

function delayPayment(uint _idPayment, uint _delay) onlySecurityGuard {
        if (_idPayment >= authorizedPayments.length) throw;

        // Overflow test
        if (_delay > 10**18) throw;

        Payment p = authorizedPayments[_idPayment];

        if ((p.securityGuardDelay + _delay > maxSecurityGuardDelay) ||
            (p.paid) ||
            (p.canceled))
            throw;

        p.securityGuardDelay += _delay;
        p.earliestPayTime += _delay;
    }


/// @notice Provides a safe ERC20.approve version for different ERC-20 implementations.
/// @param token The address of the ERC-20 token.
/// @param to The address of the user to grant spending right.
/// @param amount The token amount to grant spending right over.

function safeApprove(
        IERC20 token,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_APPROVE, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Approve failed");
    }


/// @dev Crowdfunding contract issues new tokens for address. Returns success.
/// @param _for Address of receiver.
/// @param tokenCount Number of tokens to issue.

function issueTokens(address _for, uint tokenCount)
        external
        payable
        onlyMinter
        returns (bool)
    {
        if (tokenCount == 0) {
            return false;
        }

        if (add(totalSupply, tokenCount) > maxTotalSupply) {
            throw;
        }

        totalSupply = add(totalSupply, tokenCount);
        balances[_for] = add(balances[_for], tokenCount);
        Issuance(_for, tokenCount);
        return true;
    }


/// @dev Function to change address that is allowed to do emission.
/// @param newAddress Address of new emission contract.

function changeMinter(address newAddress)
        public
        onlyFounder
        returns (bool)
    {
        // Forbid previous emission contract to distribute tokens minted during ICO stage
        delete allowed[allocationAddressICO][minter];

        minter = newAddress;

        // Allow emission contract to distribute tokens minted during ICO stage
        allowed[allocationAddressICO][minter] = balanceOf(allocationAddressICO);
    }


/// @dev Contract constructor function sets initial token balances.

function HumaniqToken(address founderAddress)
    {
        // Set founder address
        founder = founderAddress;

        // Allocate all created tokens during ICO stage to allocationAddressICO.
        balances[allocationAddressICO] = ICOSupply;

        // Allocate all created tokens during preICO stage to allocationAddressPreICO.
        balances[allocationAddressPreICO] = preICOSupply;

        // Allow founder to distribute tokens minted during preICO stage
        allowed[allocationAddressPreICO][founder] = preICOSupply;

        // Give 14 percent of all tokens to founders.
        balances[multisig] = div(mul(ICOSupply, 14), 86);

        // Set correct totalSupply and limit maximum total supply.
        totalSupply = add(ICOSupply, balances[multisig]);
        totalSupply = add(totalSupply, preICOSupply);
        maxTotalSupply = mul(totalSupply, 5);
    }


/// @dev Returns bonus for the specific moment
/// @param timestamp Time of investment (in seconds)

function getBonus(uint timestamp)
        public
        constant
        returns (uint)
    {
        if (timestamp > endDate) {
            throw;
        }

        if (startDate > timestamp) {
            return 1499;  // 49.9%
        }

        uint icoDuration = timestamp - startDate;
        if (icoDuration >= 16 days) {
            return 1000;  // 0%
        } else if (icoDuration >= 9 days) {
            return 1125;  // 12.5%
        } else if (icoDuration >= 2 days) {
            return 1250;  // 25%
        } else {
            return 1499;  // 49.9%
        }
    }


/// @dev Issues tokens for users who made BTC purchases.
/// @param beneficiary Address the tokens will be issued to.
/// @param investment Invested amount in Wei
/// @param timestamp Time of investment (in seconds)

function fixInvestment(address beneficiary, uint investment, uint timestamp)
        external
        onlyFounder
        minInvestment(investment)
        returns (uint)
    {

        // Calculate number of tokens to mint
        uint tokenCount = calculateTokens(investment, timestamp);

        // Update fund's and user's balance and total supply of tokens.
        tokensDistributed = add(tokensDistributed, tokenCount);

        // Distribute tokens.
        if (!humaniqToken.transferFrom(allocationAddress, beneficiary, tokenCount)) {
            // Tokens could not be issued.
            throw;
        }

        return tokenCount;
    }


// change restricted releaseXX account

function changeReleaseAccount(address _owner, address _newowner) internal returns (bool) {
    require(balances[_newowner] == 0);
    require(releaseTime[_owner] != 0 );
    require(releaseTime[_newowner] == 0 );
    balances[_newowner] = balances[_owner];
    releaseTime[_newowner] = releaseTime[_owner];
    balances[_owner] = 0;
    releaseTime[_owner] = 0;
    return true;
  }


/**
 * @dev Function to mint tokens
 * @param _to The address that will recieve the minted tokens.
 * @param _amount The amount of tokens to mint.
 * @param _releaseTime The (optional) freeze time - KYC & bounty accounts.
 * @return A boolean that indicates if the operation was successful.
 */

function mint(address _to, uint256 _amount, uint256 _releaseTime) internal canMint returns (bool) {
    totalSupply = totalSupply.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    if ( _releaseTime > 0 ) {
        releaseTime[_to] = _releaseTime;
    }
    emit Transfer(0x0, _to, _amount);
    return true;
  }


// ETH Course in USD
// constructor

function ArconaToken(uint256 _startSale,uint256 _finishSale,address _multisig,address _restricted,address _registerbot,address _certbot, address _release6m, address _release12m, address _release18m) public  {
        multisig = _multisig;
        restricted = _restricted;
        registerbot = _registerbot;
        certbot = _certbot;
        release6m = _release6m;
        release12m = _release12m;
        release18m = _release18m;
        startSale = _startSale;
        finishSale = _finishSale;
    }


// import preICO customers from 0x516130856e743090af9d7fd95d6fc94c8743a4e1

function importCustomer(address _customer, address _referral, uint _tokenAmount) public {
        require(msg.sender == registerbot || msg.sender == owner);
        require(_customer != address(0));
        require(now < startSale); // before ICO starts
        registered[_customer] = true;
        if (_referral != address(0) && _referral != _customer) {
            referral[_customer] = _referral;
        }
        mint(_customer, _tokenAmount, now + 99 * 1 years); // till KYC is completed
    }


/// @dev vest Detail : second unit

function vestTokensDetailInt(
                        address _beneficiary,
                        uint256 _startS,
                        uint256 _cliffS,
                        uint256 _durationS,
                        bool _revocable,
                        uint256 _tokensAmountInt) external onlyOwner {
        require(_beneficiary != address(0));

        uint256 tokensAmount = _tokensAmountInt * 10**uint256(decimals);

        if(vestingOf[_beneficiary] == 0x0) {
            TokenVesting vesting = new TokenVesting(_beneficiary, _startS, _cliffS, _durationS, _revocable, owner);
            vestingOf[_beneficiary] = address(vesting);
        }

        require(this.transferFrom(reserveTokensVault, vestingOf[_beneficiary], tokensAmount));
    }


/// @dev vest StartAt : day unit

function vestTokensStartAtInt(
                            address _beneficiary,
                            uint256 _tokensAmountInt,
                            uint256 _startS,
                            uint256 _afterDay,
                            uint256 _cliffDay,
                            uint256 _durationDay ) public onlyOwner {
        require(_beneficiary != address(0));

        uint256 tokensAmount = _tokensAmountInt * 10**uint256(decimals);
        uint256 afterSec = _afterDay * daySecond;
        uint256 cliffSec = _cliffDay * daySecond;
        uint256 durationSec = _durationDay * daySecond;

        if(vestingOf[_beneficiary] == 0x0) {
            TokenVesting vesting = new TokenVesting(_beneficiary, _startS + afterSec, cliffSec, durationSec, true, owner);
            vestingOf[_beneficiary] = address(vesting);
        }

        require(this.transferFrom(reserveTokensVault, vestingOf[_beneficiary], tokensAmount));
    }


// BTC external payments

function foreignBuy(address _holder, uint256 _weiAmount, uint256 _rate) public {
        require(msg.sender == registerbot || msg.sender == owner);
        require(_weiAmount > 0);
        require(_rate > 0);
        registered[_holder] = true;
        uint tokens = _rate.mul(_weiAmount).div(1 ether);
        mint(_holder, tokens, now + 99 * 1 years); // till KYC is completed
        totalWeiSale = totalWeiSale.add(_weiAmount);
    }


/// @notice Function to deposit stablecoins
/// @param _amount Amount to deposit
/// @param _tokenIndex Type of stablecoin to deposit

function deposit(uint256 _amount, uint256 _tokenIndex) external {
        require(msg.sender == tx.origin || isTrustedForwarder(msg.sender), "Only EOA or Biconomy");
        require(_amount > 0, "Amount must > 0");

        uint256 _ETHPrice = _determineETHPrice(_tokenIndex);
        uint256 _pool = getAllPoolInETH(_ETHPrice);
        address _sender = _msgSender();
        Tokens[_tokenIndex].token.safeTransferFrom(_sender, address(this), _amount);
        uint256 _amtDeposit = _amount; // For event purpose
        if (Tokens[_tokenIndex].decimals == 6) {
            _amount = _amount.mul(1e12);
        }

        // Calculate network fee
        uint256 _networkFeePerc;
        if (_amount < networkFeeTier2[0]) {
            // Tier 1
            _networkFeePerc = networkFeePerc[0];
        } else if (_amount <= networkFeeTier2[1]) {
            // Tier 2
            _networkFeePerc = networkFeePerc[1];
        } else if (_amount < customNetworkFeeTier) {
            // Tier 3
            _networkFeePerc = networkFeePerc[2];
        } else {
            // Custom Tier
            _networkFeePerc = customNetworkFeePerc;
        }
        uint256 _fee = _amount.mul(_networkFeePerc).div(DENOMINATOR);
        _fees = _fees.add(_fee);
        _amount = _amount.sub(_fee);

        _balanceOfDeposit[_sender] = _balanceOfDeposit[_sender].add(_amount);
        uint256 _amountInETH = _amount.mul(_ETHPrice).div(1e18);
        uint256 _shares = totalSupply() == 0 ? _amountInETH : _amountInETH.mul(totalSupply()).div(_pool);

        _mint(_sender, _shares);
        emit Deposit(address(Tokens[_tokenIndex].token), _sender, _amtDeposit, _shares);
    }


/// @notice Function to invest funds into strategy

function invest() external onlyAdmin {
        Token memory _USDT = Tokens[0];
        Token memory _USDC = Tokens[1];
        Token memory _DAI = Tokens[2];

        // Transfer out network fees
        _fees = _fees.div(1e12); // Convert to USDT decimals
        if (_fees != 0 && _USDT.token.balanceOf(address(this)) > _fees) {
            uint256 _treasuryFee =  _fees.mul(2).div(5); // 40%
            _USDT.token.safeTransfer(treasuryWallet, _treasuryFee); // 40%
            _USDT.token.safeTransfer(communityWallet, _treasuryFee); // 40%
            _USDT.token.safeTransfer(strategist, _fees.sub(_treasuryFee).sub(_treasuryFee)); // 20%
            emit TransferredOutFees(_fees);
            _fees = 0;
        }

        uint256 _poolInUSD = getAllPoolInUSD().sub(_fees);

        // Calculation for keep portion of stablecoins and swap remainder to WETH
        uint256 _toKeepUSDT = _poolInUSD.mul(_USDT.percKeepInVault).div(DENOMINATOR);
        uint256 _toKeepUSDC = _poolInUSD.mul(_USDC.percKeepInVault).div(DENOMINATOR);
        uint256 _toKeepDAI = _poolInUSD.mul(_DAI.percKeepInVault).div(DENOMINATOR);
        _invest(_USDT.token, _toKeepUSDT);
        _invest(_USDC.token, _toKeepUSDC);
        _toKeepDAI = _toKeepDAI.mul(1e12); // Follow decimals of DAI
        _invest(_DAI.token, _toKeepDAI);

        // Invest all swapped WETH to strategy
        uint256 _balanceOfWETH = WETH.balanceOf(address(this));
        if (_balanceOfWETH > 0) {
            strategy.invest(_balanceOfWETH);
            emit ETHToInvest(_balanceOfWETH);
        }
    }


/// @notice Function to determine Curve index for swapTokenWithinVault()
/// @param _tokenIndex Index of stablecoin
/// @return stablecoin index use in Curve

function _determineCurveIndex(uint256 _tokenIndex) private pure returns (int128) {
        if (_tokenIndex == 0) {
            return 2;
        } else if (_tokenIndex == 1) {
            return 1;
        } else {
            return 0;
        }
    }


/**
 * @dev Contract initializer.
 * @param _logic Address of the initial implementation.
 * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
 * It should include the signature and the parameters of the function to be called, as described in
 * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
 * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
 */

function initialize(address _logic, bytes memory _data) public payable {
        require(_implementation() == address(0));
        assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
        if (_data.length > 0) {
            (bool success, ) = _logic.delegatecall(_data);
            require(success);
        }
    }


/// @notice Function to swap between tokens with Uniswap
/// @param _amountIn Amount to swap
/// @param _fromToken Token to be swapped
/// @param _toToken Token to be received
/// @return _amounts Array that contain amount swapped

function _swapExactTokensForTokens(uint256 _amountIn, address _fromToken, address _toToken) private returns (uint256[] memory _amounts) {
        address[] memory _path = new address[](2);
        _path[0] = _fromToken;
        _path[1] = _toToken;
        uint256[] memory _amountsOut = router.getAmountsOut(_amountIn, _path);
        if (_amountsOut[1] > 0) {
            _amounts = router.swapExactTokensForTokens(_amountIn, 0, _path, address(this), block.timestamp);
        } else {
            // Not enough amount to swap
            uint256[] memory _zeroReturn = new uint256[](2);
            _zeroReturn[0] = 0;
            _zeroReturn[1] = 0;
            return _zeroReturn;
        }
    }


/// @notice Function to set new network fee for deposit amount tier 2
/// @param _networkFeeTier2 Array that contains minimum and maximum amount of tier 2 (18 decimals)

function setNetworkFeeTier2(uint256[] calldata _networkFeeTier2) external onlyOwner {
        require(_networkFeeTier2[0] != 0, "Minimun amount cannot be 0");
        require(_networkFeeTier2[1] > _networkFeeTier2[0], "Maximun amount must greater than minimun amount");
        /**
         * Network fees have three tier, but it is sufficient to have minimun and maximun amount of tier 2
         * Tier 1: deposit amount < minimun amount of tier 2
         * Tier 2: minimun amount of tier 2 <= deposit amount <= maximun amount of tier 2
         * Tier 3: amount > maximun amount of tier 2
         */
        uint256[] memory oldNetworkFeeTier2 = networkFeeTier2;
        networkFeeTier2 = _networkFeeTier2;
        emit SetNetworkFeeTier2(oldNetworkFeeTier2, _networkFeeTier2);
    }


/// @notice Function to set new network fee percentage
/// @param _networkFeePerc Array that contains new network fee percentage for tier 1, tier 2 and tier 3

function setNetworkFeePerc(uint256[] calldata _networkFeePerc) external onlyOwner {
        require(
            _networkFeePerc[0] < 3000 &&
                _networkFeePerc[1] < 3000 &&
                _networkFeePerc[2] < 3000,
            "Network fee percentage cannot be more than 30%"
        );
        /**
         * _networkFeePerc content a array of 3 element, representing network fee of tier 1, tier 2 and tier 3
         * For example networkFeePerc is [100, 75, 50]
         * which mean network fee for Tier 1 = 1%, Tier 2 = 0.75% and Tier 3 = 0.5%
         */
        uint256[] memory oldNetworkFeePerc = networkFeePerc;
        networkFeePerc = _networkFeePerc;
        emit SetNetworkFeePerc(oldNetworkFeePerc, _networkFeePerc);
    }


/// @notice Function to migrate all funds from old strategy contract to new strategy contract

function migrateFunds() external onlyOwner {
        require(unlockTime <= block.timestamp && unlockTime.add(1 days) >= block.timestamp, "Function locked");
        require(WETH.balanceOf(address(strategy)) > 0, "No balance to migrate");
        require(pendingStrategy != address(0), "No pendingStrategy");

        uint256 _amount = WETH.balanceOf(address(strategy));
        WETH.safeTransferFrom(address(strategy), pendingStrategy, _amount);

        // Set new strategy
        address oldStrategy = address(strategy);
        strategy = ICitadelStrategy(pendingStrategy);
        pendingStrategy = address(0);
        canSetPendingStrategy = true;

        // Approve new strategy
        WETH.safeApprove(address(strategy), type(uint256).max);
        WETH.safeApprove(oldStrategy, 0);

        unlockTime = 0; // Lock back this function
        emit MigrateFunds(oldStrategy, address(strategy), _amount);
    }


/// @notice Function to get exact USD amount of pool in vault
/// @return Exact USD amount of pool in vault (no decimals)

function _getVaultPoolInUSD() private view returns (uint256) {
        uint256 _vaultPoolInUSD = (Tokens[0].token.balanceOf(address(this)).mul(1e12))
            .add(Tokens[1].token.balanceOf(address(this)).mul(1e12))
            .add(Tokens[2].token.balanceOf(address(this)))
            .sub(_fees);
            // In very rare case that fees > vault pool, above calculation will raise error
            // Use getReimburseTokenAmount() to get some stablecoin from strategy
        return _vaultPoolInUSD.div(1e18);
    }


/// @notice Function to determine ETH price based on stablecoin
/// @param _tokenIndex Type of stablecoin to determine
/// @return Price of ETH (18 decimals)

function _determineETHPrice(uint256 _tokenIndex) private view returns (uint256) {
        address _priceFeedContract;
        if (address(Tokens[_tokenIndex].token) == 0xdAC17F958D2ee523a2206206994597C13D831ec7) { // USDT
            _priceFeedContract = 0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46; // USDT/ETH
        } else if (address(Tokens[_tokenIndex].token) == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) { // USDC
            _priceFeedContract = 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4; // USDC/ETH
        } else { // DAI
            _priceFeedContract = 0x773616E4d11A78F511299002da57A0a94577F1f4; // DAI/ETH
        }
        return _getPriceFromChainlink(_priceFeedContract);
    }


// ----- PUBLIC METHODS ----- //
//emits Transfer event

function mintUniqly(uint256 numUniqlies) external payable {
        require(_saleStarted, "Sale not started yet");
        uint256 requiredValue = numUniqlies * _tokenPrice;
        uint256 mintIndex = totalSupply();
        require(msg.value >= requiredValue, "Not enough ether");
        require(
            (numUniqlies + mintIndex) <= _maxUniqly,
            "You cannot buy that many tokens"
        );

        for (uint256 i = 0; i < numUniqlies; i++) {
            _safeMint(msg.sender, mintIndex);
            mintIndex++;
        }
        // send back ETH if one overpay by accident
        if (requiredValue < msg.value) {
            payable(msg.sender).transfer(msg.value - requiredValue);
        }
    }


// ----- OWNERS METHODS ----- //

function getRandomNumber(uint256 adminProvidedSeed)
        external
        onlyOwner
        returns (bytes32)
    {
        require(totalSupply() >= _maxUniqly, "Sale must be ended");
        require(randomResult == 0, "Random number already initiated");
        require(address(this).balance >= 10 ether, "min 10 ETH balance required");
        require(
            LINK.balanceOf(address(this)) >= fee,
            "Not enough LINK"
        );
        return requestRandomness(keyHash, fee, adminProvidedSeed);
    }


/**
 * @dev Returns the current implementation of a proxy.
 * This is needed because only the proxy admin can query it.
 * @return The address of the current implementation of the proxy.
 */

function getProxyImplementation(AdminUpgradeabilityProxy proxy) public view returns (address) {
    // We need to manually run the static call since the getter cannot be flagged as view
    // bytes4(keccak256("implementation()")) == 0x5c60da1b
    (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
    require(success);
    return abi.decode(returndata, (address));
  }


/**
 * @dev Returns the admin of a proxy. Only the admin can query it.
 * @return The address of the current admin of the proxy.
 */

function getProxyAdmin(AdminUpgradeabilityProxy proxy) public view returns (address) {
    // We need to manually run the static call since the getter cannot be flagged as view
    // bytes4(keccak256("admin()")) == 0xf851a440
    (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
    require(success);
    return abi.decode(returndata, (address));
  }


/*
 	 * @dev Withdraws the contracts balance to owner and DAO.
 	 */

function withdraw() public onlyOwner {
		// Reserve 30% for DAO treasury
		(bool hs, ) = payable(DAO_TREASURY_ADDRESS).call{ value: (address(this).balance * 30) / 100 }("");
		require(hs, "DAO tranfer failed");

		// owner only
		(bool os, ) = payable(owner()).call{ value: address(this).balance }("");
		require(os, "owner transfer failed");
	}