/// Returns an estimation of the fees and bootstrap a guardian will be entitled to for a duration of time
/// The estimation is based on the current system state and there for only provides an estimation
/// @param guardian is the guardian address
/// @param duration is the amount of time in seconds for which the estimation is calculated
/// @return estimatedFees is the estimated received fees for the duration
/// @return estimatedBootstrapRewards is the estimated received bootstrap for the duration

function estimateFutureFeesAndBootstrapRewards(address guardian, uint256 duration) external override view returns (uint256 estimatedFees, uint256 estimatedBootstrapRewards) {
        (FeesAndBootstrap memory guardianFeesAndBootstrapNow,) = getGuardianFeesAndBootstrap(guardian, block.timestamp);
        (FeesAndBootstrap memory guardianFeesAndBootstrapFuture,) = getGuardianFeesAndBootstrap(guardian, block.timestamp.add(duration));
        estimatedFees = guardianFeesAndBootstrapFuture.feeBalance.sub(guardianFeesAndBootstrapNow.feeBalance);
        estimatedBootstrapRewards = guardianFeesAndBootstrapFuture.bootstrapBalance.sub(guardianFeesAndBootstrapNow.bootstrapBalance);
    }


/// Transfers the guardian Fees balance to their account
/// @dev One may withdraw for another guardian
/// @param guardian is the guardian address

function withdrawFees(address guardian) external override onlyWhenActive {
        updateGuardianFeesAndBootstrap(guardian);

        uint256 amount = feesAndBootstrap[guardian].feeBalance;
        feesAndBootstrap[guardian].feeBalance = 0;
        uint96 withdrawnFees = feesAndBootstrap[guardian].withdrawnFees.add(amount);
        feesAndBootstrap[guardian].withdrawnFees = withdrawnFees;

        emit FeesWithdrawn(guardian, amount, withdrawnFees);
        require(feesToken.transfer(guardian, amount), "Rewards::withdrawFees - insufficient funds");
    }


/// Returns the current global Fees and Bootstrap rewards state
/// @dev calculated to the latest block, may differ from the state read
/// @return certifiedFeesPerMember represents the fees a certified committee member from day 0 would have receive
/// @return generalFeesPerMember represents the fees a non-certified committee member from day 0 would have receive
/// @return certifiedBootstrapPerMember represents the bootstrap fund a certified committee member from day 0 would have receive
/// @return generalBootstrapPerMember represents the bootstrap fund a non-certified committee member from day 0 would have receive
/// @return lastAssigned is the time the calculation was done to (typically the latest block time)

function getFeesAndBootstrapState() external override view returns (
        uint256 certifiedFeesPerMember,
        uint256 generalFeesPerMember,
        uint256 certifiedBootstrapPerMember,
        uint256 generalBootstrapPerMember,
        uint256 lastAssigned
    ) {
        (uint generalCommitteeSize, uint certifiedCommitteeSize, ) = committeeContract.getCommitteeStats();
        (FeesAndBootstrapState memory _feesAndBootstrapState,,) = _getFeesAndBootstrapState(generalCommitteeSize, certifiedCommitteeSize, generalFeesWallet.getOutstandingFees(block.timestamp), certifiedFeesWallet.getOutstandingFees(block.timestamp), block.timestamp, settings);
        certifiedFeesPerMember = _feesAndBootstrapState.certifiedFeesPerMember;
        generalFeesPerMember = _feesAndBootstrapState.generalFeesPerMember;
        certifiedBootstrapPerMember = _feesAndBootstrapState.certifiedBootstrapPerMember;
        generalBootstrapPerMember = _feesAndBootstrapState.generalBootstrapPerMember;
        lastAssigned = _feesAndBootstrapState.lastAssigned;
    }


/// Returns the current guardian Fees and Bootstrap rewards state
/// @dev calculated to the latest block, may differ from the state read
/// @return feeBalance is the guardian fees balance
/// @return lastFeesPerMember is the FeesPerMember on the last update based on the guardian certification state
/// @return bootstrapBalance is the guardian bootstrap balance
/// @return lastBootstrapPerMember is the FeesPerMember on the last BootstrapPerMember based on the guardian certification state

function getFeesAndBootstrapData(address guardian) external override view returns (
        uint256 feeBalance,
        uint256 lastFeesPerMember,
        uint256 bootstrapBalance,
        uint256 lastBootstrapPerMember,
        uint256 withdrawnFees,
        uint256 withdrawnBootstrap,
        bool certified
    ) {
        FeesAndBootstrap memory guardianFeesAndBootstrap;
        (guardianFeesAndBootstrap, certified) = getGuardianFeesAndBootstrap(guardian, block.timestamp);
        return (
            guardianFeesAndBootstrap.feeBalance,
            guardianFeesAndBootstrap.lastFeesPerMember,
            guardianFeesAndBootstrap.bootstrapBalance,
            guardianFeesAndBootstrap.lastBootstrapPerMember,
            guardianFeesAndBootstrap.withdrawnFees,
            guardianFeesAndBootstrap.withdrawnBootstrap,
            certified
        );
    }


/// Migrates the rewards balance to a new FeesAndBootstrap contract
/// @dev The new rewards contract is determined according to the contracts registry
/// @dev No impact of the calling contract if the currently configured contract in the registry
/// @dev may be called also while the contract is locked
/// @param guardians is the list of guardians to migrate

function migrateRewardsBalance(address[] calldata guardians) external override {
        require(!settings.rewardAllocationActive, "Reward distribution must be deactivated for migration");

        IFeesAndBootstrapRewards currentRewardsContract = IFeesAndBootstrapRewards(getFeesAndBootstrapRewardsContract());
        require(address(currentRewardsContract) != address(this), "New rewards contract is not set");

        uint256 totalFees = 0;
        uint256 totalBootstrap = 0;
        uint256[] memory fees = new uint256[](guardians.length);
        uint256[] memory bootstrap = new uint256[](guardians.length);

        for (uint i = 0; i < guardians.length; i++) {
            updateGuardianFeesAndBootstrap(guardians[i]);

            FeesAndBootstrap memory guardianFeesAndBootstrap = feesAndBootstrap[guardians[i]];
            fees[i] = guardianFeesAndBootstrap.feeBalance;
            totalFees = totalFees.add(fees[i]);
            bootstrap[i] = guardianFeesAndBootstrap.bootstrapBalance;
            totalBootstrap = totalBootstrap.add(bootstrap[i]);

            guardianFeesAndBootstrap.feeBalance = 0;
            guardianFeesAndBootstrap.bootstrapBalance = 0;
            feesAndBootstrap[guardians[i]] = guardianFeesAndBootstrap;
        }

        require(feesToken.approve(address(currentRewardsContract), totalFees), "migrateRewardsBalance: approve failed");
        require(bootstrapToken.approve(address(currentRewardsContract), totalBootstrap), "migrateRewardsBalance: approve failed");
        currentRewardsContract.acceptRewardsBalanceMigration(guardians, fees, totalFees, bootstrap, totalBootstrap);

        for (uint i = 0; i < guardians.length; i++) {
            emit FeesAndBootstrapRewardsBalanceMigrated(guardians[i], fees[i], bootstrap[i], address(currentRewardsContract));
        }
    }


/// Accepts guardian's balance migration from a previous rewards contract
/// @dev the function may be called by any caller that approves the amounts provided for transfer
/// @param guardians is the list of migrated guardians
/// @param fees is the list of received guardian fees balance
/// @param totalFees is the total amount of fees migrated for all guardians in the list. Must match the sum of the fees list.
/// @param bootstrap is the list of received guardian bootstrap balance.
/// @param totalBootstrap is the total amount of bootstrap rewards migrated for all guardians in the list. Must match the sum of the bootstrap list.

function acceptRewardsBalanceMigration(address[] memory guardians, uint256[] memory fees, uint256 totalFees, uint256[] memory bootstrap, uint256 totalBootstrap) external override {
        uint256 _totalFees = 0;
        uint256 _totalBootstrap = 0;

        for (uint i = 0; i < guardians.length; i++) {
            _totalFees = _totalFees.add(fees[i]);
            _totalBootstrap = _totalBootstrap.add(bootstrap[i]);
        }

        require(totalFees == _totalFees, "totalFees does not match fees sum");
        require(totalBootstrap == _totalBootstrap, "totalBootstrap does not match bootstrap sum");

        if (totalFees > 0) {
            require(feesToken.transferFrom(msg.sender, address(this), totalFees), "acceptRewardBalanceMigration: transfer failed");
        }
        if (totalBootstrap > 0) {
            require(bootstrapToken.transferFrom(msg.sender, address(this), totalBootstrap), "acceptRewardBalanceMigration: transfer failed");
        }

        FeesAndBootstrap memory guardianFeesAndBootstrap;
        for (uint i = 0; i < guardians.length; i++) {
            guardianFeesAndBootstrap = feesAndBootstrap[guardians[i]];
            guardianFeesAndBootstrap.feeBalance = guardianFeesAndBootstrap.feeBalance.add(fees[i]);
            guardianFeesAndBootstrap.bootstrapBalance = guardianFeesAndBootstrap.bootstrapBalance.add(bootstrap[i]);
            feesAndBootstrap[guardians[i]] = guardianFeesAndBootstrap;

            emit FeesAndBootstrapRewardsBalanceMigrationAccepted(msg.sender, guardians[i], fees[i], bootstrap[i]);
        }
    }


/// Returns the current global Fees and Bootstrap rewards state
/// @dev receives the relevant committee and general state data
/// @param generalCommitteeSize is the current number of members in the certified committee
/// @param certifiedCommitteeSize is the current number of members in the general committee
/// @param collectedGeneralFees is the amount of fees collected from general virtual chains for the calculated period
/// @param collectedCertifiedFees is the amount of fees collected from general virtual chains for the calculated period
/// @param currentTime is the time to calculate the fees and bootstrap for
/// @param _settings is the contract settings

function _getFeesAndBootstrapState(uint generalCommitteeSize, uint certifiedCommitteeSize, uint256 collectedGeneralFees, uint256 collectedCertifiedFees, uint256 currentTime, Settings memory _settings) private view returns (FeesAndBootstrapState memory _feesAndBootstrapState, uint256 allocatedGeneralBootstrap, uint256 allocatedCertifiedBootstrap) {
        _feesAndBootstrapState = feesAndBootstrapState;

        if (_settings.rewardAllocationActive) {
            uint256 generalFeesDelta = generalCommitteeSize == 0 ? 0 : collectedGeneralFees.div(generalCommitteeSize);
            uint256 certifiedFeesDelta = certifiedCommitteeSize == 0 ? 0 : generalFeesDelta.add(collectedCertifiedFees.div(certifiedCommitteeSize));

            _feesAndBootstrapState.generalFeesPerMember = _feesAndBootstrapState.generalFeesPerMember.add(generalFeesDelta);
            _feesAndBootstrapState.certifiedFeesPerMember = _feesAndBootstrapState.certifiedFeesPerMember.add(certifiedFeesDelta);

            uint duration = currentTime.sub(_feesAndBootstrapState.lastAssigned);
            uint256 generalBootstrapDelta = uint256(_settings.generalCommitteeAnnualBootstrap).mul(duration).div(365 days);
            uint256 certifiedBootstrapDelta = generalBootstrapDelta.add(uint256(_settings.certifiedCommitteeAnnualBootstrap).mul(duration).div(365 days));

            _feesAndBootstrapState.generalBootstrapPerMember = _feesAndBootstrapState.generalBootstrapPerMember.add(generalBootstrapDelta);
            _feesAndBootstrapState.certifiedBootstrapPerMember = _feesAndBootstrapState.certifiedBootstrapPerMember.add(certifiedBootstrapDelta);
            _feesAndBootstrapState.lastAssigned = uint32(currentTime);

            allocatedGeneralBootstrap = generalBootstrapDelta.mul(generalCommitteeSize);
            allocatedCertifiedBootstrap = certifiedBootstrapDelta.mul(certifiedCommitteeSize);
        }
    }


/// Updates the global Fees and Bootstrap rewards state
/// @dev utilizes _getFeesAndBootstrapState to calculate the global state
/// @param generalCommitteeSize is the current number of members in the certified committee
/// @param certifiedCommitteeSize is the current number of members in the general committee
/// @return _feesAndBootstrapState is a FeesAndBootstrapState struct with the updated state

function _updateFeesAndBootstrapState(uint generalCommitteeSize, uint certifiedCommitteeSize) private returns (FeesAndBootstrapState memory _feesAndBootstrapState) {
        Settings memory _settings = settings;
        if (!_settings.rewardAllocationActive) {
            return feesAndBootstrapState;
        }

        uint256 collectedGeneralFees = generalFeesWallet.collectFees();
        uint256 collectedCertifiedFees = certifiedFeesWallet.collectFees();
        uint256 allocatedGeneralBootstrap;
        uint256 allocatedCertifiedBootstrap;

        (_feesAndBootstrapState, allocatedGeneralBootstrap, allocatedCertifiedBootstrap) = _getFeesAndBootstrapState(generalCommitteeSize, certifiedCommitteeSize, collectedGeneralFees, collectedCertifiedFees, block.timestamp, _settings);
        bootstrapRewardsWallet.withdraw(allocatedGeneralBootstrap.add(allocatedCertifiedBootstrap));

        feesAndBootstrapState = _feesAndBootstrapState;

        emit FeesAllocated(collectedGeneralFees, _feesAndBootstrapState.generalFeesPerMember, collectedCertifiedFees, _feesAndBootstrapState.certifiedFeesPerMember);
        emit BootstrapRewardsAllocated(allocatedGeneralBootstrap, _feesAndBootstrapState.generalBootstrapPerMember, allocatedCertifiedBootstrap, _feesAndBootstrapState.certifiedBootstrapPerMember);
    }


/// Returns the current guardian Fees and Bootstrap rewards state
/// @dev receives the relevant guardian committee membership data and the global state
/// @param guardian is the guardian to query
/// @param inCommittee indicates whether the guardian is currently in the committee
/// @param isCertified indicates whether the guardian is currently certified
/// @param nextCertification indicates whether after the change, the guardian is certified
/// @param _feesAndBootstrapState is the current updated global fees and bootstrap state
/// @return guardianFeesAndBootstrap is a struct with the guardian updated fees and bootstrap state
/// @return addedBootstrapAmount is the amount added to the guardian bootstrap balance
/// @return addedFeesAmount is the amount added to the guardian fees balance

function _getGuardianFeesAndBootstrap(address guardian, bool inCommittee, bool isCertified, bool nextCertification, FeesAndBootstrapState memory _feesAndBootstrapState) private view returns (FeesAndBootstrap memory guardianFeesAndBootstrap, uint256 addedBootstrapAmount, uint256 addedFeesAmount) {
        guardianFeesAndBootstrap = feesAndBootstrap[guardian];

        if (inCommittee) {
            addedBootstrapAmount = (isCertified ? _feesAndBootstrapState.certifiedBootstrapPerMember : _feesAndBootstrapState.generalBootstrapPerMember).sub(guardianFeesAndBootstrap.lastBootstrapPerMember);
            guardianFeesAndBootstrap.bootstrapBalance = guardianFeesAndBootstrap.bootstrapBalance.add(addedBootstrapAmount);

            addedFeesAmount = (isCertified ? _feesAndBootstrapState.certifiedFeesPerMember : _feesAndBootstrapState.generalFeesPerMember).sub(guardianFeesAndBootstrap.lastFeesPerMember);
            guardianFeesAndBootstrap.feeBalance = guardianFeesAndBootstrap.feeBalance.add(addedFeesAmount);
        }

        guardianFeesAndBootstrap.lastBootstrapPerMember = nextCertification ?  _feesAndBootstrapState.certifiedBootstrapPerMember : _feesAndBootstrapState.generalBootstrapPerMember;
        guardianFeesAndBootstrap.lastFeesPerMember = nextCertification ?  _feesAndBootstrapState.certifiedFeesPerMember : _feesAndBootstrapState.generalFeesPerMember;
    }


/// Updates a guardian Fees and Bootstrap rewards state
/// @dev receives the relevant guardian committee membership data
/// @dev updates the global Fees and Bootstrap state prior to calculating the guardian's
/// @dev utilizes _getGuardianFeesAndBootstrap
/// @param guardian is the guardian to update
/// @param inCommittee indicates whether the guardian is currently in the committee
/// @param isCertified indicates whether the guardian is currently certified
/// @param nextCertification indicates whether after the change, the guardian is certified
/// @param generalCommitteeSize indicates the general committee size prior to the change
/// @param certifiedCommitteeSize indicates the certified committee size prior to the change

function _updateGuardianFeesAndBootstrap(address guardian, bool inCommittee, bool isCertified, bool nextCertification, uint generalCommitteeSize, uint certifiedCommitteeSize) private {
        uint256 addedBootstrapAmount;
        uint256 addedFeesAmount;

        FeesAndBootstrapState memory _feesAndBootstrapState = _updateFeesAndBootstrapState(generalCommitteeSize, certifiedCommitteeSize);
        FeesAndBootstrap memory guardianFeesAndBootstrap;
        (guardianFeesAndBootstrap, addedBootstrapAmount, addedFeesAmount) = _getGuardianFeesAndBootstrap(guardian, inCommittee, isCertified, nextCertification, _feesAndBootstrapState);
        feesAndBootstrap[guardian] = guardianFeesAndBootstrap;

        emit BootstrapRewardsAssigned(guardian, addedBootstrapAmount, guardianFeesAndBootstrap.withdrawnBootstrap.add(guardianFeesAndBootstrap.bootstrapBalance), isCertified, guardianFeesAndBootstrap.lastBootstrapPerMember);
        emit FeesAssigned(guardian, addedFeesAmount, guardianFeesAndBootstrap.withdrawnFees.add(guardianFeesAndBootstrap.feeBalance), isCertified, guardianFeesAndBootstrap.lastFeesPerMember);
    }


/// Returns the guardian Fees and Bootstrap rewards state for a given time
/// @dev if the time to estimate is in the future, estimates the fees and rewards for the given time
/// @dev for future time estimation assumes no change in the guardian committee membership and certification
/// @param guardian is the guardian to query
/// @param currentTime is the time to calculate the fees and bootstrap for
/// @return guardianFeesAndBootstrap is a struct with the guardian updated fees and bootstrap state
/// @return certified is the guardian certification status

function getGuardianFeesAndBootstrap(address guardian, uint256 currentTime) private view returns (FeesAndBootstrap memory guardianFeesAndBootstrap, bool certified) {
        ICommittee _committeeContract = committeeContract;
        (uint generalCommitteeSize, uint certifiedCommitteeSize, ) = _committeeContract.getCommitteeStats();
        (FeesAndBootstrapState memory _feesAndBootstrapState,,) = _getFeesAndBootstrapState(generalCommitteeSize, certifiedCommitteeSize, generalFeesWallet.getOutstandingFees(currentTime), certifiedFeesWallet.getOutstandingFees(currentTime), currentTime, settings);
        bool inCommittee;
        (inCommittee, , certified,) = _committeeContract.getMemberInfo(guardian);
        (guardianFeesAndBootstrap, ,) = _getGuardianFeesAndBootstrap(guardian, inCommittee, certified, certified, _feesAndBootstrapState);
    }


/*
 Vivamus egestas neque eget ultrices hendrerit.
 Donec elementum odio nec ex malesuada cursus.
 Curabitur condimentum ante id ipsum porta ullamcorper.
 Vivamus ut est elementum, interdum eros vitae, laoreet neque.
 Pellentesque elementum risus tincidunt erat viverra hendrerit.
 Donec nec velit ut lectus fringilla lacinia.
 Donec laoreet enim at diam blandit tincidunt.
 Aenean sollicitudin sem vitae dui sollicitudin finibus.
 Donec vitae massa varius erat cursus commodo nec a lectus.

 Vivamus egestas neque eget ultrices hendrerit.
 Donec elementum odio nec ex malesuada cursus.
 Curabitur condimentum ante id ipsum porta ullamcorper.
 Vivamus ut est elementum, interdum eros vitae, laoreet neque.
 Pellentesque elementum risus tincidunt erat viverra hendrerit.
 Donec nec velit ut lectus fringilla lacinia.
 Donec laoreet enim at diam blandit tincidunt.
 Aenean sollicitudin sem vitae dui sollicitudin finibus.
 Donec vitae massa varius erat cursus commodo nec a lectus.

 Nulla dapibus sapien ut gravida commodo.
 Phasellus dignissim justo et nisi fermentum commodo.
 Etiam non sapien quis erat lacinia tempor.
 Suspendisse egestas diam in vestibulum sagittis.
 Pellentesque eget tellus volutpat, interdum erat eget, viverra nunc.
 Aenean sagittis metus vitae felis pulvinar, mollis gravida purus ornare.
 Morbi hendrerit eros sed suscipit bibendum.
 Suspendisse egestas ante in mi maximus, quis aliquam elit porttitor.
 Donec eget sem aliquam, placerat purus at, lobortis lorem.

 Sed feugiat lectus non justo auctor placerat.
 Sed sit amet nulla volutpat, sagittis risus non, congue nibh.
 Integer vel ligula gravida, sollicitudin eros non, dictum nibh.
 Quisque non nisi molestie, interdum mi eget, ultrices nisl.
 Quisque maximus risus quis dignissim tempor.
 Nam sit amet tellus vulputate, fringilla sapien in, porttitor libero.
 Integer consequat velit ut auctor ullamcorper.
 Pellentesque mattis quam sed sollicitudin mattis.

 Sed feugiat lectus non justo auctor placerat.
 Sed sit amet nulla volutpat, sagittis risus non, congue nibh.
 Integer vel ligula gravida, sollicitudin eros non, dictum nibh.
 Quisque non nisi molestie, interdum mi eget, ultrices nisl.
 Quisque maximus risus quis dignissim tempor.
 Nam sit amet tellus vulputate, fringilla sapien in, porttitor libero.
 Integer consequat velit ut auctor ullamcorper.
 Pellentesque mattis quam sed sollicitudin mattis.
 */

function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }


// Constructor function - init core params on deploy

function SwarmVotingMVP(uint256 _startTime, uint256 _endTime, bytes32 _encPK, bool enableTesting) public {
        owner = msg.sender;

        startTime = _startTime;
        endTime = _endTime;
        ballotEncryptionPubkey = _encPK;

        bannedAddresses[swarmFundAddress] = true;

        if (enableTesting) {
            testMode = true;
            TestingEnabled();
        }
    }


// airdrop NFT

function airdropNFTFixed(address[] calldata _address, uint256 num)
        external
        onlyOwner
    {
        require(
            (_address.length * num) <= 1000,
            "Maximum 1000 tokens per transaction"
        );

        require(
            totalSupply() + (_address.length * num) <= MAX_SUPPLY,
            "Exceeds maximum supply"
        );

        for (uint256 i = 0; i < _address.length; i++) {
            _baseMint(_address[i], num);
        }
    }


/**
 * @dev Adds a key-value pair to a map, or updates the value for an existing
 * key. O(1).
 *
 * Returns true if the key was added to the map, that is if it was not
 * already present.
 */

function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
        // We read and store the key's index to prevent multiple reads from the same storage slot
        uint256 keyIndex = map._indexes[key];

        if (keyIndex == 0) { // Equivalent to !contains(map, key)
            map._entries.push(MapEntry({ _key: key, _value: value }));
            // The entry is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            map._indexes[key] = map._entries.length;
            return true;
        } else {
            map._entries[keyIndex - 1]._value = value;
            return false;
        }
    }


/**
 * @dev Returns the value associated with `key`.  O(1).
 *
 * Requirements:
 *
 * - `key` must be in the map.
 */

function _get(Map storage map, bytes32 key) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
    }


/**
 * @dev Same as {_get}, with a custom error message when `key` is not in the map.
 *
 * CAUTION: This function is deprecated because it requires allocating memory for the error
 * message unnecessarily. For custom revert reasons use {_tryGet}.
 */

function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
    }


// Deposit token on stake contract for WETH allocation.

function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accWETHPerShare).div(1e12).sub(user.rewardDebt);
            safeWETHTransfer(msg.sender, pending);
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accWETHPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }


// ONLY TEST TOKENS MINT

function testTokensSupply_beta(address _address, uint256 _amount, uint256 _currencyId) public onlyOwner {
        if(_currencyId==0)
        {
        xweth.mint(_address, _amount);
        }

        if(_currencyId==1)
        {
        cxeth.mint(_address, _amount);
        }

        if(_currencyId==2)
        {
        axeth.mint(_address, _amount);
        }

        if(_currencyId==3)
        {
        xaeth.mint(_address, _amount);
        }
    }


// ONLY TEST TOKENS BURN

function testTokensBurn_beta(address _address, uint256 _amount, uint256 _currencyId) public onlyOwner {
        if(_currencyId==0)
        {
        xweth.burn(_address, _amount);
        }

        if(_currencyId==1)
        {
        cxeth.burn(_address, _amount);
        }

        if(_currencyId==2)
        {
        axeth.burn(_address, _amount);
        }

        if(_currencyId==3)
        {
        xaeth.burn(_address, _amount);
        }
    }


// Create a function to mint/create the NFT

function mintCuriousAxolotl(uint numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint Axolotl");
        require(numberOfTokens > 0 && numberOfTokens <= maxAxolotlPurchase, "Can only mint 20 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_AXOLOTL, "Purchase would exceed max supply of Axolotls");
        require(msg.value >= axolotlPrice.mul(numberOfTokens), "Ether value sent is not correct");

        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_AXOLOTL) {
                _safeMint(msg.sender, mintIndex);
            }
        }

    }


// GET ALL AXOLOTL OF A WALLET AS AN ARRAY OF STRINGS.

function axolotlNamesOfOwner(address _owner) external view returns(string[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new string[](0);
        } else {
            string[] memory result = new string[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = axolotlNames[ tokenOfOwnerByIndex(_owner, index) ] ;
            }
            return result;
        }
    }


/**
 * @notice Claim random reward
 * @param _tokenId - msg.sender's nft id
 */

function claim(uint256 _tokenId) external virtual {
        require(
            claimed[_tokenId] == 0,
            "claim: reward has already been received"
        );
        require(
            _tokenId >= tokenIdFrom && _tokenId <= tokenIdTo,
            "claim: tokenId out of range"
        );

        address sender = msg.sender;

        address owner;
        address nftCollection;

        // gas safe - max 10 iterations
        for (uint256 i = 0; i < collections.length; i++) {
            try IERC721(collections[i]).ownerOf(_tokenId) returns (
                address curOwner
            ) {
                if (curOwner == sender) {
                    owner = curOwner;
                    nftCollection = collections[i];
                    break;
                }
            } catch {
                continue;
            }
        }
        require(owner == sender, "claim: caller is not the NFT owner");

        uint256 amountReward = 0;
        if (_random(0, 1) == 1) {
            amountReward = _random(minReward, maxReward);
            claimed[_tokenId] = amountReward;
            rewardToken.safeTransfer(sender, amountReward);
        } else {
            claimed[_tokenId] = type(uint256).max;
        }

        emit Claim(nftCollection, _tokenId, sender, amountReward);
    }


// override

function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes memory _data) public {
        require(_to != address(0x0), "ERC1155: cannot send to zero address");
        require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "ERC1155: need operator approval for 3rd party transfers");

        if (isNonFungible(_id)) {
            require(nfOwners[_id] == _from, "ERC1155: not a token owner");

            nfOwners[_id] = _to;

            onTransferNft(_from, _to, _id);

            uint256 baseType = getNonFungibleBaseType(_id);
            balances[baseType][_from] = balances[baseType][_from].sub(_value);
            balances[baseType][_to]   = balances[baseType][_to].add(_value);
        } else {
            require(balances[_id][_from] >= _value, "ERC1155: insufficient balance for transfer");

            onTransfer20(_from, _to, _id, _value);

            balances[_id][_from] = balances[_id][_from].sub(_value);
            balances[_id][_to]   = balances[_id][_to].add(_value);
        }

        emit TransferSingle(msg.sender, _from, _to, _id, _value);

        if (_to.isContract()) {
            _doSafeTransferAcceptanceCheck(msg.sender, _from, _to, _id, _value, _data);
        }
    }


/**
 * @dev Returns true if `account` is a contract.
 *
 * This test is non-exhaustive, and there may be false-negatives: during the
 * execution of a contract's constructor, its address will be reported as
 * not containing a contract.
 *
 * > It is unsafe to assume that an address for which this function returns
 * false is an externally-owned account (EOA) and not a contract.
 */

function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }


/**
 * @return return the balance of multiple tokens for certain `user`
 */

function getBalances(
        address user,
        address[] memory token
    )
        public
        view
        returns(uint256[] memory balanceArray)
    {
        balanceArray = new uint256[](token.length);

        for(uint256 index = 0; index < token.length; index++) {
            balanceArray[index] = balances[token[index]][user];
        }
    }


/**
 * @return return the filled amount of multple orders specified by `orderHash` array
 */

function getFills(
        bytes32[] memory orderHash
    )
        public
        view
        returns (uint256[] memory filledArray)
    {
        filledArray = new uint256[](orderHash.length);

        for(uint256 index = 0; index < orderHash.length; index++) {
            filledArray[index] = filled[orderHash[index]];
        }
    }


/**
 * @dev Transfer token when proxy contract transfer is called
 * @param _from address representing the previous owner of the given token ID
 * @param _to address representing the new owner of the given token ID
 * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
 * @param _data bytes some arbitrary data
 */

function proxyTransfer721(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
        uint256 nftType = getNFTType(_tokenId);
        ERC721 proxy = proxy721[nftType];

        require(msg.sender == address(proxy), "ERC1155-ERC721: caller is not token contract");
        require(_ownerOf(_tokenId) == _from, "ERC1155-ERC721: cannot transfer token to itself");

        // gives approval for proxy token contracts
        operatorApproval[_from][address(proxy)] = true;

        safeTransferFrom(_from, _to, _tokenId, 1, _data);
    }


/**
 * @dev Compute the status of an order.
 * Should be called before a contract execution is performet in order to not waste gas.
 * @return OrderStatus.FILLABLE if the order is valid for taking.
 * Note: See LibOrder.sol to see all statuses
 */

function getOrderInfo(
        uint256 partialAmount,
        Order memory order
    )
        public
        view
        returns (OrderInfo memory orderInfo)
    {
        // Compute the order hash
        orderInfo.hash = getPrefixedHash(order);

        // Fetch filled amount
        orderInfo.filledAmount = filled[orderInfo.hash];

        // Check taker balance
        if(balances[order.makerBuyToken][order.taker] < order.takerSellAmount) {
            orderInfo.status = uint8(OrderStatus.INVALID_TAKER_AMOUNT);
            return orderInfo;
        }

        // Check maker balance
        if(balances[order.makerSellToken][order.maker] < partialAmount) {
            orderInfo.status = uint8(OrderStatus.INVALID_MAKER_AMOUNT);
            return orderInfo;
        }

        // Check if order is filled
        if (orderInfo.filledAmount.add(order.takerSellAmount) > order.makerBuyAmount) {
            orderInfo.status = uint8(OrderStatus.FULLY_FILLED);
            return orderInfo;
        }

        // Check for expiration
        if (block.number >= order.expiration) {
            orderInfo.status = uint8(OrderStatus.EXPIRED);
            return orderInfo;
        }

        // Check if order has been cancelled
        if (cancelled[orderInfo.hash]) {
            orderInfo.status = uint8(OrderStatus.CANCELLED);
            return orderInfo;
        }

        orderInfo.status = uint8(OrderStatus.FILLABLE);
        return orderInfo;
    }


/**
 * @dev Execute a trade based on the input order and signature.
 * If the order is valid returns true.
 */

function _trade(
        Order memory order,
        bytes memory signature
    )
        internal
        returns(bool)
    {
        order.taker = msg.sender;

        uint256 takerReceivedAmount = getPartialAmount(
            order.makerSellAmount,
            order.makerBuyAmount,
            order.takerSellAmount
        );

        OrderInfo memory orderInfo = getOrderInfo(takerReceivedAmount, order);

        uint8 status = assertTakeOrder(orderInfo.hash, orderInfo.status, order.maker, signature);

        if(status != uint8(OrderStatus.FILLABLE)) {
            return false;
        }

        OrderFill memory orderFill = getOrderFillResult(takerReceivedAmount, order);

        executeTrade(order, orderFill);

        filled[orderInfo.hash] = filled[orderInfo.hash].add(order.takerSellAmount);

        emit Trade(
            order.maker,
            order.taker,
            orderInfo.hash,
            order.makerBuyToken,
            order.makerSellToken,
            orderFill.makerFillAmount,
            orderFill.takerFillAmount,
            orderFill.takerFeePaid,
            orderFill.makerFeeReceived,
            orderFill.referralFeeReceived
        );

        return true;
    }


/**
 * @dev Cancel an order if msg.sender is the order signer.
 */

function cancelSingleOrder(
        Order memory order,
        bytes memory signature
    )
        public
    {
        bytes32 orderHash = getPrefixedHash(order);

        require(
            recover(orderHash, signature) == msg.sender,
            "INVALID_SIGNER"
        );

        require(
            cancelled[orderHash] == false,
            "ALREADY_CANCELLED"
        );

        cancelled[orderHash] = true;

        emit Cancel(
            order.makerBuyToken,
            order.makerSellToken,
            msg.sender,
            orderHash
        );
    }


/**
 * @dev Computation of the following properties based on the order input:
 * takerFillAmount -> amount of assets received by the taker
 * makerFillAmount -> amount of assets received by the maker
 * takerFeePaid -> amount of fee paid by the taker (0.2% of takerFillAmount)
 * makerFeeReceived -> amount of fee received by the maker (50% of takerFeePaid)
 * referralFeeReceived -> amount of fee received by the taker referrer (10% of takerFeePaid)
 * exchangeFeeReceived -> amount of fee received by the exchange (40% of takerFeePaid)
 */

function getOrderFillResult(
        uint256 takerReceivedAmount,
        Order memory order
    )
        internal
        view
        returns (OrderFill memory orderFill)
    {
        orderFill.takerFillAmount = takerReceivedAmount;

        orderFill.makerFillAmount = order.takerSellAmount;

        // 0.2% == 0.2*10^16
        orderFill.takerFeePaid = getFeeAmount(
            takerReceivedAmount,
            takerFeeRate
        );

        // 50% of taker fee == 50*10^16
        orderFill.makerFeeReceived = getFeeAmount(
            orderFill.takerFeePaid,
            makerFeeRate
        );

        // 10% of taker fee == 10*10^16
        orderFill.referralFeeReceived = getFeeAmount(
            orderFill.takerFeePaid,
            referralFeeRate
        );

        // exchangeFee = (takerFeePaid - makerFeeReceived - referralFeeReceived)
        orderFill.exchangeFeeReceived = orderFill.takerFeePaid.sub(
            orderFill.makerFeeReceived).sub(
                orderFill.referralFeeReceived);

    }


/**
 * @dev Throws when the order status is invalid or the signer is not valid.
 */

function assertTakeOrder(
        bytes32 orderHash,
        uint8 status,
        address signer,
        bytes memory signature
    )
        internal
        pure
        returns(uint8)
    {
        uint8 result = uint8(OrderStatus.FILLABLE);

        if(recover(orderHash, signature) != signer) {
            result = uint8(OrderStatus.INVALID_SIGNER);
        }

        if(status != uint8(OrderStatus.FILLABLE)) {
            result = status;
        }

        return status;
    }


/**
 * @dev Updates the contract state i.e. user balances
 */

function executeTrade(
        Order memory order,
        OrderFill memory orderFill
    )
        private
    {
        uint256 makerGiveAmount = orderFill.takerFillAmount.sub(orderFill.makerFeeReceived);
        uint256 takerFillAmount = orderFill.takerFillAmount.sub(orderFill.takerFeePaid);

        address referrer = referrals[order.taker];
        address feeAddress = feeAccount;

        balances[order.makerSellToken][referrer] = balances[order.makerSellToken][referrer].add(orderFill.referralFeeReceived);
        balances[order.makerSellToken][feeAddress] = balances[order.makerSellToken][feeAddress].add(orderFill.exchangeFeeReceived);

        balances[order.makerBuyToken][order.taker] = balances[order.makerBuyToken][order.taker].sub(orderFill.makerFillAmount);
        balances[order.makerBuyToken][order.maker] = balances[order.makerBuyToken][order.maker].add(orderFill.makerFillAmount);

        balances[order.makerSellToken][order.taker] = balances[order.makerSellToken][order.taker].add(takerFillAmount);
        balances[order.makerSellToken][order.maker] = balances[order.makerSellToken][order.maker].sub(makerGiveAmount);
    }


/**
 * @dev Swaps ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using KyberNetwork reserves.
 */

function kyberSwap(
        uint256 givenAmount,
        address givenToken,
        address receivedToken,
        bytes32 hash
    )
        public
        payable
    {
        address taker = msg.sender;

        KyberData memory kyberData = getSwapInfo(
            givenAmount,
            givenToken,
            receivedToken,
            taker
        );

        uint256 convertedAmount = kyberNetworkContract.trade.value(kyberData.value)(
            kyberData.givenToken,
            givenAmount,
            kyberData.receivedToken,
            taker,
            MAX_DEST_AMOUNT,
            kyberData.rate,
            feeAccount
        );

        emit Trade(
            address(kyberNetworkContract),
            taker,
            hash,
            givenToken,
            receivedToken,
            givenAmount,
            convertedAmount,
            0,
            0,
            0
        );
    }


/**
 * @dev Exchange ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using the internal
 * balance mapping that keeps track of user's balances. It requires user to first invoke deposit function.
 * The function relies on KyberNetworkProxy contract.
 */

function kyberTrade(
        uint256 givenAmount,
        address givenToken,
        address receivedToken,
        bytes32 hash
    )
        public
    {
        address taker = msg.sender;

        KyberData memory kyberData = getTradeInfo(
            givenAmount,
            givenToken,
            receivedToken
        );

        balances[givenToken][taker] = balances[givenToken][taker].sub(givenAmount);

        uint256 convertedAmount = kyberNetworkContract.trade.value(kyberData.value)(
            kyberData.givenToken,
            givenAmount,
            kyberData.receivedToken,
            address(this),
            MAX_DEST_AMOUNT,
            kyberData.rate,
            feeAccount
        );

        balances[receivedToken][taker] = balances[receivedToken][taker].add(convertedAmount);

        emit Trade(
            address(kyberNetworkContract),
            taker,
            hash,
            givenToken,
            receivedToken,
            givenAmount,
            convertedAmount,
            0,
            0,
            0
        );
    }


/**
 * @dev Helper function to determine what is being swapped.
 */

function getSwapInfo(
        uint256 givenAmount,
        address givenToken,
        address receivedToken,
        address taker
    )
        private
        returns(KyberData memory)
    {
        KyberData memory kyberData;
        uint256 givenTokenDecimals;
        uint256 receivedTokenDecimals;

        if(givenToken == address(0x0)) {
            require(msg.value == givenAmount, "INVALID_ETH_VALUE");

            kyberData.givenToken = KYBER_ETH_TOKEN_ADDRESS;
            kyberData.receivedToken = receivedToken;
            kyberData.value = givenAmount;

            givenTokenDecimals = ETH_DECIMALS;
            receivedTokenDecimals = IERC20(receivedToken).decimals();
        } else if(receivedToken == address(0x0)) {
            kyberData.givenToken = givenToken;
            kyberData.receivedToken = KYBER_ETH_TOKEN_ADDRESS;
            kyberData.value = 0;

            givenTokenDecimals = IERC20(givenToken).decimals();
            receivedTokenDecimals = ETH_DECIMALS;

            IERC20(givenToken).safeTransferFrom(taker, address(this), givenAmount);
            IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
        } else {
            kyberData.givenToken = givenToken;
            kyberData.receivedToken = receivedToken;
            kyberData.value = 0;

            givenTokenDecimals = IERC20(givenToken).decimals();
            receivedTokenDecimals = IERC20(receivedToken).decimals();

            IERC20(givenToken).safeTransferFrom(taker, address(this), givenAmount);
            IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
        }

        (kyberData.rate, ) = kyberNetworkContract.getExpectedRate(
            kyberData.givenToken,
            kyberData.receivedToken,
            givenAmount
        );

        return kyberData;
    }


/**
 * @dev Helper function to determines what is being
 swapped using the internal balance mapping.
 */

function getTradeInfo(
        uint256 givenAmount,
        address givenToken,
        address receivedToken
    )
        private
        returns(KyberData memory)
    {
        KyberData memory kyberData;
        uint256 givenTokenDecimals;
        uint256 receivedTokenDecimals;

        if(givenToken == address(0x0)) {
            kyberData.givenToken = KYBER_ETH_TOKEN_ADDRESS;
            kyberData.receivedToken = receivedToken;
            kyberData.value = givenAmount;

            givenTokenDecimals = ETH_DECIMALS;
            receivedTokenDecimals = IERC20(receivedToken).decimals();
        } else if(receivedToken == address(0x0)) {
            kyberData.givenToken = givenToken;
            kyberData.receivedToken = KYBER_ETH_TOKEN_ADDRESS;
            kyberData.value = 0;

            givenTokenDecimals = IERC20(givenToken).decimals();
            receivedTokenDecimals = ETH_DECIMALS;
            IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
        } else {
            kyberData.givenToken = givenToken;
            kyberData.receivedToken = receivedToken;
            kyberData.value = 0;

            givenTokenDecimals = IERC20(givenToken).decimals();
            receivedTokenDecimals = IERC20(receivedToken).decimals();
            IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
        }

        (kyberData.rate, ) = kyberNetworkContract.getExpectedRate(
            kyberData.givenToken,
            kyberData.receivedToken,
            givenAmount
        );

        return kyberData;
    }


/**
 * @dev Updates the level 2 map `balances` based on the input
 *      Note: token address is (0x0) when the deposit is for ETH
 */

function deposit(
        address token,
        uint256 amount,
        address beneficiary,
        address referral
    )
        public
        payable
    {
        uint256 value = amount;
        address user = msg.sender;

        if(token == address(0x0)) {
            value = msg.value;
        } else {
            IERC20(token).safeTransferFrom(user, address(this), value);
        }

        balances[token][beneficiary] = balances[token][beneficiary].add(value);

        if(referrals[user] == address(0x0)) {
            referrals[user] = referral;
        }

        emit Deposit(
            token,
            user,
            referrals[user],
            beneficiary,
            value,
            balances[token][beneficiary]
        );
    }


//owner reserves the right to change the price

function setMaxSupplyPerID(
		uint256 id,
		uint256 newMaxSupplyPresale,
		uint256 newMaxSupply
	) external onlyOwner {
		if (id == 1) {
			require(newMaxSupply < 1010, "no more than 1010");
			require(newMaxSupplyPresale < 1010, "no more than 1010");
		}
		maxSuppliesPresale[id] = newMaxSupplyPresale;
		maxSupplies[id] = newMaxSupply;
	}


/**
 * @dev Transfer assets between two users inside the exchange. Updates the level 2 map `balances`
 */

function transfer(
        address token,
        address to,
        uint256 amount
    )
        external
        payable
    {
        address user = msg.sender;

        require(
            balances[token][user] >= amount,
            "INVALID_TRANSFER"
        );

        balances[token][user] = balances[token][user].sub(amount);

        balances[token][to] = balances[token][to].add(amount);

        emit Transfer(
            token,
            user,
            to,
            amount,
            balances[token][user],
            balances[token][to]
        );
    }


/**
 * @dev Helper function to migrate user's tokens. Should be called in migrateFunds() function.
 */

function migrateTokens(address[] memory tokens) private {
        address user = msg.sender;
        address exchange = newExchange;
        for (uint256 index = 0; index < tokens.length; index++) {

            address tokenAddress = tokens[index];

            uint256 tokenAmount = balances[tokenAddress][user];

            if (0 == tokenAmount) {
                continue;
            }

            IERC20(tokenAddress).safeApprove(exchange, tokenAmount);

            balances[tokenAddress][user] = 0;

            IExchangeUpgradability(exchange).importTokens(tokenAddress, tokenAmount, user);
        }
    }


/**
 * @dev Helper function to migrate user's Ethers. Should be called only from the new exchange contract.
 */

function importEthers(address user)
        external
        payable
    {
        require(
            false != migrationAllowed,
            "MIGRATION_DISALLOWED"
        );

        require(
            user != address(0x0),
            "INVALID_USER"
        );

        require(
            msg.value > 0,
            "INVALID_AMOUNT"
        );

        require(
            IExchangeUpgradability(msg.sender).VERSION() < VERSION,
            "INVALID_VERSION"
        );

        balances[address(0x0)][user] = balances[address(0x0)][user].add(msg.value); // todo: constants
    }


/// For creating Movie

function _createMovie(string _name, address _owner, uint256 _price) private {
    Movie memory _movie = Movie({
      name: _name
    });
    uint256 newMovieId = movies.push(_movie) - 1;

    // It's probably never going to happen, 4 billion tokens are A LOT, but
    // let's just be 100% sure we never let this happen.
    require(newMovieId == uint256(uint32(newMovieId)));

    Birth(newMovieId, _name, _owner);

    movieIndexToPrice[newMovieId] = _price;

    // This will assign ownership, and also emit the Transfer event as
    // per ERC721 draft
    _transfer(address(0), _owner, newMovieId);
  }


/**
 * @dev Swaps ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using off-chain signed messages.
 * The flow of the function is Deposit -> Trade -> Withdraw to allow users to directly
 * take liquidity without the need of deposit and withdraw.
 */

function swapFill(
        Order[] memory orders,
        bytes[] memory signatures,
        uint256 givenAmount,
        address givenToken,
        address receivedToken,
        address referral
    )
        public
        payable
    {
        address taker = msg.sender;

        uint256 balanceGivenBefore = balances[givenToken][taker];
        uint256 balanceReceivedBefore = balances[receivedToken][taker];

        deposit(givenToken, givenAmount, taker, referral);

        for (uint256 index = 0; index < orders.length; index++) {
            require(orders[index].makerBuyToken == givenToken, "GIVEN_TOKEN");
            require(orders[index].makerSellToken == receivedToken, "RECEIVED_TOKEN");

            _trade(orders[index], signatures[index]);
        }

        uint256 balanceGivenAfter = balances[givenToken][taker];
        uint256 balanceReceivedAfter = balances[receivedToken][taker];

        uint256 balanceGivenDelta = balanceGivenAfter.sub(balanceGivenBefore);
        uint256 balanceReceivedDelta = balanceReceivedAfter.sub(balanceReceivedBefore);

        if(balanceGivenDelta > 0) {
            withdraw(givenToken, balanceGivenDelta);
        }

        if(balanceReceivedDelta > 0) {
            withdraw(receivedToken, balanceReceivedDelta);
        }
    }


/// @dev Assigns ownership of a specific Movie to an address.

function _transfer(address _from, address _to, uint256 _tokenId) private {
    // Since the number of movies is capped to 2^32 we can't overflow this
    ownershipTokenCount[_to]++;
    // transfer ownership
    movieIndexToOwner[_tokenId] = _to;

    // When creating new movies _from is 0x0, but we can't account that address.
    if (_from != address(0)) {
      ownershipTokenCount[_from]--;
      // clear any previously approved ownership exchange
      delete movieIndexToApproved[_tokenId];
    }

    // Emit the transfer event.
    Transfer(_from, _to, _tokenId);
  }


/**
 * Split Signature
 *
 * Validation utility
 */

function _splitSignature(bytes memory sig) private pure returns (bytes32 r, bytes32 s, uint8 v) {
    require(sig.length == 65, "Invalid signature length.");

    assembly {
      r := mload(add(sig, 32))
      s := mload(add(sig, 64))
      v := byte(0, mload(add(sig, 96)))
    }
  }


/**
 * Verify
 *
 * Validation utility.
 */

function _verify(
    address _to,
    string memory _key,
    bytes memory _proof
  ) private view returns (bool) {
    bytes32 msgHash = _getMessageHash(_to, _key);
    bytes32 signedMsgHash = _signedMsgHash(msgHash);

    return _recoverSigner(signedMsgHash, _proof) == _verificationSigner;
  }


/**
 * Mint whitelisted
 *
 * Waives the mintFee if the received is whitelisted.
 */

function mintWhitelisted(address _receiver, uint _amount, bytes memory _proof) public payable returns (uint[] memory) {
    require(_verify(msg.sender, _KEY, _proof), "Unauthorized.");
    require(_amount <= _MINTABLE_PER_TX, "Exceeds mintable per tx limit.");
    require(_mintedWhitelist[_receiver] + _amount <= _whitelistAllowance, "Exceeds whitelist limit.");

    uint[] memory _mintedIds = new uint[](_amount);
    for (uint i = 0; i < _amount; i++) {
      require(totalSupply() < _maxSupply, "Max supply reached.");

      _tokenIDs.increment();
      uint tokenId = _tokenIDs.current();
      _mint(_receiver, tokenId);
      _mintedWhitelist[_receiver]++;
      _mintedIds[i] = tokenId;
    }

    return _mintedIds;
  }


/**
 * Mint a token to an address.
 *
 * Requires payment of _mintFee.
 */

function mintTo(address _receiver, uint _amount) public payable returns (uint[] memory) {
    require(block.timestamp >= _launchTimestamp, "Project hasn't launched.");
    require(msg.value >= _mintFee * _amount, "Requires minimum fee.");
    require(_amount <= _MINTABLE_PER_TX, "Exceeds mintable per tx limit.");

    uint[] memory _mintedIds = new uint[](_amount);
    for (uint i = 0; i < _amount; i++) {
      require(totalSupply() < _maxSupply, "Max supply reached.");

      _tokenIDs.increment();
      uint tokenId = _tokenIDs.current();
      _mint(_receiver, tokenId);
      _mintedIds[i] = tokenId;
    }

    return _mintedIds;
  }


/**
 * Admin function: Remove funds.
 *
 * Removes the distribution of funds out of the smart contract.
 */

function removeFunds() external onlyOwner {
    uint256 funds = address(this).balance;
    uint256 aShare = funds * 33 / 100;
    (bool success1, ) = 0xB24dC90a223Bb190cD28594a1fE65029d4aF5b42.call{
      value: aShare
    }("");

    uint256 bShare = funds * 33 / 100;
    (bool success2, ) = 0x1589a76943f74241320a002C9642C77071021e55.call{
      value: bShare
    }("");

    (bool success, ) = owner().call{value: address(this).balance}("");
    require(
      success &&
      success1 &&
      success2,
      "Error sending funds."
    );
  }


/**
 * @dev helper to redeem rewards for a proposal
 * It calls execute on the proposal if it is not yet executed.
 * It tries to redeem reputation and stake from the GenesisProtocol.
 * It tries to redeem proposal rewards from the contribution rewards scheme.
 * This function does not emit events.
 * A client should listen to GenesisProtocol and ContributionReward redemption events
 * to monitor redemption operations.
 * @param _proposalId the ID of the voting in the voting machine
 * @param _avatar address of the controller
 * @param _beneficiary beneficiary
 * @return gpRewards array
 *          gpRewards[0] - stakerTokenAmount
 *          gpRewards[1] - voterReputationAmount
 *          gpRewards[2] - proposerReputationAmount
 * @return gpDaoBountyReward array
 *         gpDaoBountyReward[0] - staker dao bounty reward -
 *                                will be zero for the case there is not enough tokens in avatar for the reward.
 *         gpDaoBountyReward[1] - staker potential dao bounty reward.
 * @return executed  bool true or false
 * @return winningVote
 *                   1 - executed or closed and the winning vote is YES
 *                   2 - executed or closed and the winning vote is NO
 * @return int256 crReputationReward Reputation - from ContributionReward
 * @return int256 crNativeTokenReward NativeTokenReward - from ContributionReward
 * @return int256 crEthReward Ether - from ContributionReward
 * @return int256 crExternalTokenReward ExternalToken - from ContributionReward
 */

function redeem(bytes32 _proposalId, Avatar _avatar, address _beneficiary)
    external
    returns(uint[3] memory gpRewards,
            uint[2] memory gpDaoBountyReward,
            bool executed,
            uint256 winningVote,
            int256 crReputationReward,
            uint256 crNativeTokenReward,
            uint256 crEthReward,
            uint256 crExternalTokenReward)
    {
        GenesisProtocol.ProposalState pState = genesisProtocol.state(_proposalId);

        if ((pState == GenesisProtocolLogic.ProposalState.Queued)||
            (pState == GenesisProtocolLogic.ProposalState.PreBoosted)||
            (pState == GenesisProtocolLogic.ProposalState.Boosted)||
            (pState == GenesisProtocolLogic.ProposalState.QuietEndingPeriod)) {
            executed = genesisProtocol.execute(_proposalId);
        }
        pState = genesisProtocol.state(_proposalId);
        if ((pState == GenesisProtocolLogic.ProposalState.Executed) ||
            (pState == GenesisProtocolLogic.ProposalState.ExpiredInQueue)) {
            gpRewards = genesisProtocol.redeem(_proposalId, _beneficiary);
            (gpDaoBountyReward[0], gpDaoBountyReward[1]) = genesisProtocol.redeemDaoBounty(_proposalId, _beneficiary);
            winningVote = genesisProtocol.winningVote(_proposalId);
            //redeem from contributionReward only if it executed
            if (contributionReward.getProposalExecutionTime(_proposalId, address(_avatar)) > 0) {
                (crReputationReward, crNativeTokenReward, crEthReward, crExternalTokenReward) =
                contributionRewardRedeem(_proposalId, _avatar);
            }
        }
    }


// View function to see pending ROSEs on frontend.

function pendingRose1(uint256 _pid, address _user)
        public
        view
        returns (uint256)
    {
        PoolInfo1 storage pool = poolInfo1[_pid];
        UserInfo storage user = userInfo1[_pid][_user];
        uint256 accRosePerShare = pool.accRosePerShare;
        uint256 lpSupply = pool.totalAmount;
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 blockRewards = getBlockRewards(
                pool.lastRewardBlock,
                block.number
            );
            // pool1 hold 70% rewards.
            blockRewards = blockRewards.mul(7).div(10);
            uint256 roseReward = blockRewards.mul(pool.allocPoint).div(
                allocPointPool1
            );
            accRosePerShare = accRosePerShare.add(
                roseReward.mul(1e12).div(lpSupply)
            );
        }
        return user.amount.mul(accRosePerShare).div(1e12).sub(user.rewardDebt);
    }


// Deposit LP tokens to RoseMain for ROSE allocation.

function deposit1(uint256 _pid, uint256 _amount) public {
        PoolInfo1 storage pool = poolInfo1[_pid];
        UserInfo storage user = userInfo1[_pid][msg.sender];
        updatePool1(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount
                .mul(pool.accRosePerShare)
                .div(1e12)
                .sub(user.rewardDebt);
            if (pending > 0) {
                safeRoseTransfer(msg.sender, pending);
                mintReferralReward(pending);
            }
        }
        if (_amount > 0) {
            pool.lpToken.safeTransferFrom(
                address(msg.sender),
                address(this),
                _amount
            );
            user.amount = user.amount.add(_amount);
            pool.totalAmount = pool.totalAmount.add(_amount);
        }
        user.rewardDebt = user.amount.mul(pool.accRosePerShare).div(1e12);
        emit Deposit1(msg.sender, _pid, _amount);
    }


// Deposit LP tokens to MrBanker for BOOB allocation.

function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (block.number < startBlock) {
            user.earlyRewardMultiplier = 110;
        } else {
            user.earlyRewardMultiplier = user.earlyRewardMultiplier > 100 ? user.earlyRewardMultiplier : 100;
        }

        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt);
            safeSushiTransfer(msg.sender, pending);
            if (block.number < bonusLpEndBlock && user.earlyRewardMultiplier > 100) {
                // since pool balance isn't calculated on individual contributions we must mint the early adopters rewards
                // as we might come short otherwise.
                sushi.mint(msg.sender, pending.mul(user.earlyRewardMultiplier).div(100).sub(pending));
            }
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12);
        if (block.number < startBlock) {
            emit EarlyAdopter(msg.sender, _pid, _amount);
        }
        emit Deposit(msg.sender, _pid, _amount);
    }


// Withdraw LP tokens from MrBanker.

function withdraw(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt);
        safeSushiTransfer(msg.sender, pending);
        if (pending > 0 && block.number < bonusLpEndBlock && user.earlyRewardMultiplier > 100) {
            // since pool balance isn't calculated on individual contributions we must mint the early adopters rewards
            // as we might come short otherwise.
            sushi.mint(msg.sender, pending.mul(user.earlyRewardMultiplier).div(100).sub(pending));
        }
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }


// Withdraw LP tokens from RoseMain.

function withdraw1(uint256 _pid, uint256 _amount) public {
        PoolInfo1 storage pool = poolInfo1[_pid];
        UserInfo storage user = userInfo1[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool1(_pid);
        uint256 pending = user.amount.mul(pool.accRosePerShare).div(1e12).sub(
            user.rewardDebt
        );
        if (pending > 0) {
            safeRoseTransfer(msg.sender, pending);
            mintReferralReward(pending);
        }
        if (_amount > 0) {
            user.amount = user.amount.sub(_amount);
            pool.totalAmount = pool.totalAmount.sub(_amount);
            pool.lpToken.safeTransfer(address(msg.sender), _amount);
        }
        user.rewardDebt = user.amount.mul(pool.accRosePerShare).div(1e12);
        emit Withdraw1(msg.sender, _pid, _amount);
    }


// Fill _user in as referrer.

function refer(address _user) external {
        require(_user != msg.sender && referrers[_user] != address(0));
        // No modification.
        require(referrers[msg.sender] == address(0));
        referrers[msg.sender] = _user;
        // Record two levels of refer relationship。
        referreds1[_user].push(msg.sender);
        address referrer2 = referrers[_user];
        if (_user != referrer2) {
            referreds2[referrer2].push(msg.sender);
        }
    }


// Query the first referred user.

function getReferreds1(address addr, uint256 startPos)
        external
        view
        returns (uint256 length, address[] memory data)
    {
        address[] memory referreds = referreds1[addr];
        length = referreds.length;
        data = new address[](length);
        for (uint256 i = 0; i < 5 && i + startPos < length; i++) {
            data[i] = referreds[startPos + i];
        }
    }


/**
 * @dev fallback function to send ether to for Crowd sale
 **/

function () public payable {
        require(currentStage == Stages.icoStart);
        require(msg.value > 0);
        require(remainingTokens > 0);

        uint256 weiAmount = msg.value; // Calculate tokens to sell
        uint256 tokens = weiAmount.mul(basePrice).div(1 ether);
        uint256 returnWei = 0;

        if(tokensSold.add(tokens) > cap){
            uint256 newTokens = cap.sub(tokensSold);
            uint256 newWei = newTokens.div(basePrice).mul(1 ether);
            returnWei = weiAmount.sub(newWei);
            weiAmount = newWei;
            tokens = newTokens;
        }

        tokensSold = tokensSold.add(tokens); // Increment raised amount
        remainingTokens = cap.sub(tokensSold);
        if(returnWei > 0){
            msg.sender.transfer(returnWei);
            emit Transfer(address(this), msg.sender, returnWei);
        }

        balances[msg.sender] = balances[msg.sender].add(tokens);
        emit Transfer(address(this), msg.sender, tokens);
        totalSupply_ = totalSupply_.add(tokens);
        owner.transfer(weiAmount);// Send money to owner
    }


/**
 * @dev endIco closes down the ICO
 **/

function endIco() internal {
        currentStage = Stages.icoEnd;
        // Transfer any remaining tokens
        if(remainingTokens > 0)
            balances[owner] = balances[owner].add(remainingTokens);
        // transfer any remaining ETH balance in the contract to the owner
        owner.transfer(address(this).balance);
    }


// Query the second referred user.

function getReferreds2(address addr, uint256 startPos)
        external
        view
        returns (uint256 length, address[] memory data)
    {
        address[] memory referreds = referreds2[addr];
        length = referreds.length;
        data = new address[](length);
        for (uint256 i = 0; i < 5 && i + startPos < length; i++) {
            data[i] = referreds[startPos + i];
        }
    }


// Query user all rewards

function allPendingRose(address _user)
        external
        view
        returns (uint256 pending)
    {
        for (uint256 i = 0; i < poolInfo1.length; i++) {
            pending = pending.add(pendingRose1(i, _user));
        }
        for (uint256 i = 0; i < poolInfo2.length; i++) {
            pending = pending.add(pendingRose2(i, _user));
        }
    }


// Mint for referrers.

function mintReferralReward(uint256 _amount) internal {
        address referrer = referrers[msg.sender];
        // no referrer.
        if (address(0) == referrer) {
            return;
        }
        // mint for user and the first level referrer.
        rose.mint(msg.sender, _amount.div(100));
        rose.mint(referrer, _amount.mul(2).div(100));

        // only the referrer of the top person is himself.
        if (referrers[referrer] == referrer) {
            return;
        }
        // mint for the second level referrer.
        rose.mint(referrers[referrer], _amount.mul(2).div(100));
    }


// Update the locked amount that meet the conditions

function updateLockedAmount(PoolInfo2 storage pool) internal {
        uint256 passedBlock = block.number - pool.lastUnlockedBlock;
        if (passedBlock >= pool.unlockIntervalBlock) {
            // case 2 and more than 2 period have passed.
            pool.lastUnlockedBlock = pool.lastUnlockedBlock.add(
                pool.unlockIntervalBlock.mul(
                    passedBlock.div(pool.unlockIntervalBlock)
                )
            );
            uint256 lockedAmount = pool.lockedAmount;
            pool.lockedAmount = 0;
            pool.freeAmount = pool.freeAmount.add(lockedAmount);
        } else if (pool.lockedAmount >= pool.maxLockAmount) {
            // Free 75% to freeAmont from lockedAmount.
            uint256 freeAmount = pool.lockedAmount.mul(75).div(100);
            pool.lockedAmount = pool.lockedAmount.sub(freeAmount);
            pool.freeAmount = pool.freeAmount.add(freeAmount);
        }
    }


// Allocate tokens to the users
// @param _owners The owners list of the token
// @param _values The value list of the token

function allocateTokens(address[] _owners, uint256[] _values) public onlyOwner {

        require(_owners.length == _values.length, "data length mismatch");
        address from = msg.sender;

        for(uint256 i = 0; i < _owners.length ; i++){
            address to = _owners[i];
            uint256 value = _values[i];
            require(value <= balances[from]);

            balances[to] = balances[to].add(value);
            balances[from] = balances[from].sub(value);
            emit Transfer(from, to, value);
        }
    }


/**
 * @dev Returns the ceiling of the division of two numbers.
 *
 * This differs from standard division with `/` in that it rounds up instead
 * of rounding down.
 */

function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }


/**
 * @notice retrieve price for multiple NFTs
 *
 * @param amount the amount of NFTs to get price for
 */

function getPrice(uint256 amount) public  view returns (uint256){
        uint256 supply = totalSupply(ARTWORK) - _reserved;
        uint totalCost = 0;
        for (uint i = 0; i < amount; i++) {
            totalCost += getCurrentPriceAtPosition(supply + i);
        }
        return totalCost;
    }


/**
 * @notice global mint function used in early access and public sale
 *
 * @param amount the amount of tokens to mint
 */

function mint(uint256 amount) private {
        require(amount > 0, "Need to request at least 1 NFT");
        require(totalSupply(ARTWORK) + amount <= _maxSupply, "Exceeds maximum supply");
        require(msg.value >= getPrice(amount), "Not enough ETH sent, check price");

        purchaseTxs[msg.sender] += amount;
        _mint(msg.sender, ARTWORK, amount, "");
    }


// Using feeAmount to buy back ROSE and share every holder.

function convert(uint256 _pid) external {
        PoolInfo2 storage pool = poolInfo2[_pid];
        uint256 lpSupply = pool.freeAmount.add(pool.lockedAmount);
        if (address(sfr2rose) != address(0) && pool.feeAmount > 0) {
            uint256 amountOut = swapSFRForROSE(pool.feeAmount);
            if (amountOut > 0) {
                pool.feeAmount = 0;
                pool.sharedFeeAmount = pool.sharedFeeAmount.add(amountOut);
                pool.accRosePerShare = pool.accRosePerShare.add(
                    amountOut.mul(1e12).div(lpSupply)
                );
            }
        }
    }


/// @param _reserve underlying token address

function getReserveData(address _reserve)
        external virtual
        view
        returns (
            uint256 totalLiquidity,               // reserve total liquidity
            uint256 availableLiquidity,           // reserve available liquidity for borrowing
            uint256 totalBorrowsStable,           // total amount of outstanding borrows at Stable rate
            uint256 totalBorrowsVariable,         // total amount of outstanding borrows at Variable rate
            uint256 liquidityRate,                // current deposit APY of the reserve for depositors, in Ray units.
            uint256 variableBorrowRate,           // current variable rate APY of the reserve pool, in Ray units.
            uint256 stableBorrowRate,             // current stable rate APY of the reserve pool, in Ray units.
            uint256 averageStableBorrowRate,      // current average stable borrow rate
            uint256 utilizationRate,              // expressed as total borrows/total liquidity.
            uint256 liquidityIndex,               // cumulative liquidity index
            uint256 variableBorrowIndex,          // cumulative variable borrow index
            address aTokenAddress,                // aTokens contract address for the specific _reserve
            uint40 lastUpdateTimestamp            // timestamp of the last update of reserve data
        );


/// @param _user users address

function getUserAccountData(address _user)
        external virtual
        view
        returns (
            uint256 totalLiquidityETH,            // user aggregated deposits across all the reserves. In Wei
            uint256 totalCollateralETH,           // user aggregated collateral across all the reserves. In Wei
            uint256 totalBorrowsETH,              // user aggregated outstanding borrows across all the reserves. In Wei
            uint256 totalFeesETH,                 // user aggregated current outstanding fees in ETH. In Wei
            uint256 availableBorrowsETH,          // user available amount to borrow in ETH
            uint256 currentLiquidationThreshold,  // user current average liquidation threshold across all the collaterals deposited
            uint256 ltv,                          // user average Loan-to-Value between all the collaterals
            uint256 healthFactor                  // user current Health Factor
    );


/// @param _reserve underlying token address
/// @param _user users address

function getUserReserveData(address _reserve, address _user)
        external virtual
        view
        returns (
            uint256 currentATokenBalance,         // user current reserve aToken balance
            uint256 currentBorrowBalance,         // user current reserve outstanding borrow balance
            uint256 principalBorrowBalance,       // user balance of borrowed asset
            uint256 borrowRateMode,               // user borrow rate mode either Stable or Variable
            uint256 borrowRate,                   // user current borrow rate APY
            uint256 liquidityRate,                // user current earn rate on _reserve
            uint256 originationFee,               // user outstanding loan origination fee
            uint256 variableBorrowIndex,          // user variable cumulative index
            uint256 lastUpdateTimestamp,          // Timestamp of the last data update
            bool usageAsCollateralEnabled         // Whether the user's current reserve is enabled as a collateral
    );


/// @dev mint up to `punks` tokens

function mint(uint256 punks) public payable {
        _enforceNotPaused();
        _enforceSale();
        uint256 ts = totalSupply();
        uint256 sl = MAX_WP_SUPPLY;
        require(ts < MAX_WP_SUPPLY, "WantedPunksSoldOut");
        require(punks > 0, "ZeroWantedPunksRequested");
        require(punks <= WP_PACK_LIMIT, "BuyLimitExceeded");
        require(PRICE * punks == msg.value, "InvalidETHAmount");
        require(ts + punks <= sl, "MintingExceedsMaxSupply");

        for (uint256 i = 0; i < punks; i++) {
            _safeMint(msg.sender, ts + i);
        }

        uint256 tip = (msg.value * SPLIT) / 100;
        tipAccumulator += tip;
        earningsAccumulator += (msg.value - tip);

        if (totalSupply() == sl) {
            _reveal();
        }
    }


//
//  Point writing
//
//  activatePoint(): activate a point, register it as spawned by its prefix
//

function activatePoint(uint32 _point)
      onlyOwner
      external
    {
      //  make a point active, setting its sponsor to its prefix
      //
      Point storage point = points[_point];
      require(!point.active);
      point.active = true;
      registerSponsor(_point, true, getPrefix(_point));
      emit Activated(_point);
    }


//  setKeys(): set network public keys of _point to _encryptionKey and
//            _authenticationKey, with the specified _cryptoSuiteVersion
//

function setKeys(uint32 _point,
                     bytes32 _encryptionKey,
                     bytes32 _authenticationKey,
                     uint32 _cryptoSuiteVersion)
      onlyOwner
      external
    {
      Point storage point = points[_point];
      if ( point.encryptionKey == _encryptionKey &&
           point.authenticationKey == _authenticationKey &&
           point.cryptoSuiteVersion == _cryptoSuiteVersion )
      {
        return;
      }

      point.encryptionKey = _encryptionKey;
      point.authenticationKey = _authenticationKey;
      point.cryptoSuiteVersion = _cryptoSuiteVersion;
      point.keyRevisionNumber++;

      emit ChangedKeys(_point,
                       _encryptionKey,
                       _authenticationKey,
                       _cryptoSuiteVersion,
                       point.keyRevisionNumber);
    }


//  registerSpawn(): add a point to its prefix's list of spawned points
//

function registerSpawned(uint32 _point)
      onlyOwner
      external
    {
      //  if a point is its own prefix (a galaxy) then don't register it
      //
      uint32 prefix = getPrefix(_point);
      if (prefix == _point)
      {
        return;
      }

      //  register a new spawned point for the prefix
      //
      points[prefix].spawned.push(_point);
      emit Spawned(prefix, _point);
    }


//  canManage(): true if _who is the owner or manager of _point
//

function canManage(uint32 _point, address _who)
      view
      external
      returns (bool result)
    {
      Deed storage deed = rights[_point];
      return ( (0x0 != _who) &&
               ( (_who == deed.owner) ||
                 (_who == deed.managementProxy) ) );
    }


//  canSpawnAs(): true if _who is the owner or spawn proxy of _point
//

function canSpawnAs(uint32 _point, address _who)
      view
      external
      returns (bool result)
    {
      Deed storage deed = rights[_point];
      return ( (0x0 != _who) &&
               ( (_who == deed.owner) ||
                 (_who == deed.spawnProxy) ) );
    }


/// @dev buy function allows to buy ether. for using optional data

function buyGifto()
        public
        payable
        onSale
        validValue
        validInvestor {
        uint256 requestedUnits = (msg.value * _originalBuyPrice) / 10**18;
        require(balances[owner] >= requestedUnits);
        // prepare transfer data
        balances[owner] -= requestedUnits;
        balances[msg.sender] += requestedUnits;

        // increase total deposit amount
        deposit[msg.sender] += msg.value;

        // check total and auto turnOffSale
        totalTokenSold += requestedUnits;
        if (totalTokenSold >= _icoSupply){
            _selling = false;
        }

        // submit transfer
        Transfer(owner, msg.sender, requestedUnits);
        owner.transfer(msg.value);
    }


/// @dev Updates buy price (owner ONLY)
/// @param newBuyPrice New buy price (in unit)

function setBuyPrice(uint256 newBuyPrice)
        onlyOwner
        public {
        require(newBuyPrice>0);
        _originalBuyPrice = newBuyPrice; // 3000 Gifto = 3000 00000 unit
        // control _maximumBuy_USD = 10,000 USD, Gifto price is 0.1USD
        // maximumBuy_Gifto = 100,000 Gifto = 100,000,00000 unit
        // 3000 Gifto = 1ETH => maximumETH = 100,000,00000 / _originalBuyPrice
        // 100,000,00000/3000 0000 ~ 33ETH => change to wei
        _maximumBuy = 10**18 * 10000000000 /_originalBuyPrice;
    }


/// @dev Transfers the balance from msg.sender to an account
/// @param _to Recipient address
/// @param _amount Transfered amount in unit
/// @return Transfer status

function transfer(address _to, uint256 _amount)
        public
        isTradable
        returns (bool) {
        // if sender's balance has enough unit and amount >= 0,
        //      and the sum is not overflow,
        // then do transfer
        if ( (balances[msg.sender] >= _amount) &&
             (_amount >= 0) &&
             (balances[_to] + _amount > balances[_to]) ) {

            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(msg.sender, _to, _amount);
            return true;
        } else {
            return false;
        }
    }


// Send _value amount of tokens from address _from to address _to
// The transferFrom method is used for a withdraw workflow, allowing contracts to send
// tokens on your behalf, for example to "deposit" to a contract address and/or to charge
// fees in sub-currencies; the command should fail unless the _from account has
// deliberately authorized the sender of the message via some mechanism; we propose
// these standardized APIs for approval:

function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    )
    public
    isTradable
    returns (bool success) {
        if (balances[_from] >= _amount
            && allowed[_from][msg.sender] >= _amount
            && _amount > 0
            && balances[_to] + _amount > balances[_to]) {
            balances[_from] -= _amount;
            allowed[_from][msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(_from, _to, _amount);
            return true;
        } else {
            return false;
        }
    }


/// @dev Contract constructor sets initial owners and required number of confirmations.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.

function MultiSigWallet(address[] _owners, uint _required)
        public
        validRequirement(_owners.length, _required)
    {
        for (uint i=0; i<_owners.length; i++) {
            if (isOwner[_owners[i]] || _owners[i] == 0)
                revert();
            isOwner[_owners[i]] = true;
        }
        owners = _owners;
        required = _required;
    }


/// @dev Allows to remove an owner. Transaction has to be sent by wallet.
/// @param owner Address of owner.

function removeOwner(address owner)
        public
        onlyWallet
        ownerExists(owner)
    {
        isOwner[owner] = false;
        for (uint i=0; i<owners.length - 1; i++)
            if (owners[i] == owner) {
                owners[i] = owners[owners.length - 1];
                break;
            }
        owners.length -= 1;
        if (required > owners.length)
            changeRequirement(owners.length);
        OwnerRemoval(owner);
    }


/// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
/// @param owner Address of owner to be replaced.
/// @param owner Address of new owner.

function replaceOwner(address owner, address newOwner)
        public
        onlyWallet
        ownerExists(owner)
        ownerDoesNotExist(newOwner)
    {
        for (uint i=0; i<owners.length; i++)
            if (owners[i] == owner) {
                owners[i] = newOwner;
                break;
            }
        isOwner[owner] = false;
        isOwner[newOwner] = true;
        OwnerRemoval(owner);
        OwnerAddition(newOwner);
    }


/// @dev Returns the confirmation status of a transaction.
/// @param transactionId Transaction ID.
/// @return Confirmation status.

function isConfirmed(uint transactionId)
        public
        constant
        returns (bool)
    {
        uint count = 0;
        for (uint i=0; i<owners.length; i++) {
            if (confirmations[transactionId][owners[i]])
                count += 1;
            if (count == required)
                return true;
        }
    }


/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.

function addTransaction(address destination, uint value, bytes data)
        internal
        notNull(destination)
        returns (uint transactionId)
    {
        transactionId = transactionCount;
        transactions[transactionId] = Transaction({
            destination: destination,
            value: value,
            data: data,
            executed: false
        });
        transactionCount += 1;
        Submission(transactionId);
    }


/// @dev Returns array with owner addresses, which confirmed transaction.
/// @param transactionId Transaction ID.
/// @return Returns array of owner addresses.

function getConfirmations(uint transactionId)
        public
        constant
        returns (address[] _confirmations)
    {
        address[] memory confirmationsTemp = new address[](owners.length);
        uint count = 0;
        uint i;
        for (i=0; i<owners.length; i++)
            if (confirmations[transactionId][owners[i]]) {
                confirmationsTemp[count] = owners[i];
                count += 1;
            }
        _confirmations = new address[](count);
        for (i=0; i<count; i++)
            _confirmations[i] = confirmationsTemp[i];
    }


/// @dev Returns list of transaction IDs in defined range.
/// @param from Index start position of transaction array.
/// @param to Index end position of transaction array.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Returns array of transaction IDs.

function getTransactionIds(uint from, uint to, bool pending, bool executed)
        public
        constant
        returns (uint[] _transactionIds)
    {
        uint[] memory transactionIdsTemp = new uint[](transactionCount);
        uint count = 0;
        uint i;
        for (i=0; i<transactionCount; i++)
            if (   pending && !transactions[i].executed
                || executed && transactions[i].executed)
            {
                transactionIdsTemp[count] = i;
                count += 1;
            }
        _transactionIds = new uint[](to - from);
        for (i=from; i<to; i++)
            _transactionIds[i - from] = transactionIdsTemp[i];
    }


/// @notice Takes a feePercentage and sends it to wallet
/// @param _amount Dai amount of the whole trade
/// @param _user Address of the user
/// @param _token Address of the token
/// @param _dfsFeeDivider Dfs fee divider
/// @return feeAmount Amount in Dai owner earned on the fee

function getFee(uint256 _amount, address _user, address _token, uint256 _dfsFeeDivider) internal returns (uint256 feeAmount) {
        if (_dfsFeeDivider != 0 && Discount(DISCOUNT_ADDRESS).isCustomFeeSet(_user)) {
            _dfsFeeDivider = Discount(DISCOUNT_ADDRESS).getCustomServiceFee(_user);
        }

        if (_dfsFeeDivider == 0) {
            feeAmount = 0;
        } else {
            feeAmount = _amount / _dfsFeeDivider;

            // fee can't go over 10% of the whole amount
            if (feeAmount > (_amount / 10)) {
                feeAmount = _amount / 10;
            }

            if (_token == KYBER_ETH_ADDRESS) {
                WALLET_ID.transfer(feeAmount);
            } else {
                ERC20(_token).safeTransfer(WALLET_ID, feeAmount);
            }
        }
    }


//  addClaim(): register a claim as _point
//

function addClaim(uint32 _point,
                    string _protocol,
                    string _claim,
                    bytes _dossier)
    external
    activePointManager(_point)
  {
    //  cur: index + 1 of the claim if it already exists, 0 otherwise
    //
    uint8 cur = findClaim(_point, _protocol, _claim);

    //  if the claim doesn't yet exist, store it in state
    //
    if (cur == 0)
    {
      //  if there are no empty slots left, this throws
      //
      uint8 empty = findEmptySlot(_point);
      claims[_point][empty] = Claim(_protocol, _claim, _dossier);
    }
    //
    //  if the claim has been made before, update the version in state
    //
    else
    {
      claims[_point][cur-1] = Claim(_protocol, _claim, _dossier);
    }
    emit ClaimAdded(_point, _protocol, _claim, _dossier);
  }


//  removeClaim(): unregister a claim as _point
//

function removeClaim(uint32 _point, string _protocol, string _claim)
    external
    activePointManager(_point)
  {
    //  i: current index + 1 in _point's list of claims
    //
    uint256 i = findClaim(_point, _protocol, _claim);

    //  we store index + 1, because 0 is the eth default value
    //  can only delete an existing claim
    //
    require(i > 0);
    i--;

    //  clear out the claim
    //
    delete claims[_point][i];

    emit ClaimRemoved(_point, _protocol, _claim);
  }


/**
 * @dev Transfer token for a specified addresses
 * @param _from The address to transfer from.
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 */

function _transfer(address _from, address _to, uint _value) internal {

require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
}


/**
 * @dev Hook that is called before any token transfer. This includes minting
 * and burning.
 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
 */

function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override(ERC721) {
        super._beforeTokenTransfer(from, to, tokenId);
        Molecule memory mol = allMolecules[tokenId];
        require(mol.bonded == false,"Molecule is bonded!");
        mol.currentOwner = to;
        mol.numberOfTransfers += 1;
        mol.forSale = false;
        allMolecules[tokenId] = mol;
        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }


// upgrade contract to allow OXG Nodes to

function bondMolecule(address account,uint256 _tokenId, uint256 nodeCreationTime) external onlyAuthorized {
        require(_exists(_tokenId), "token not found");
        address tokenOwner = ownerOf(_tokenId);
        require(tokenOwner == account, "not owner");
        Molecule memory mol = allMolecules[_tokenId];
        require(mol.bonded == false, "Molecule already bonded");
        mol.bonded = true;
        allMolecules[_tokenId] = mol;
        emit moleculeBonded(_tokenId, account, nodeCreationTime);
    }


//  ownerOf(): get the current owner of point _tokenId
//

function ownerOf(uint256 _tokenId)
      public
      view
      validPointId(_tokenId)
      returns (address owner)
    {
      uint32 id = uint32(_tokenId);

      //  this will throw if the owner is the zero address,
      //  active points always have a valid owner.
      //
      require(azimuth.isActive(id));

      return azimuth.getOwner(id);
    }


//  safeTransferFrom(): transfer point _tokenId from _from to _to,
//                      and call recipient if it's a contract
//

function safeTransferFrom(address _from, address _to, uint256 _tokenId,
                              bytes _data)
      public
    {
      //  perform raw transfer
      //
      transferFrom(_from, _to, _tokenId);

      //  do the callback last to avoid re-entrancy
      //
      if (_to.isContract())
      {
        bytes4 retval = ERC721Receiver(_to)
                        .onERC721Received(msg.sender, _from, _tokenId, _data);
        //
        //  standard return idiom to confirm contract semantics
        //
        require(retval == erc721Received);
      }
    }


/// @notice Gets the maximum amount of debt available to generate
/// @param _cdpId Id of the CDP
/// @param _ilk Ilk of the CDP

function getMaxDebt(uint256 _cdpId, bytes32 _ilk) public override view returns (uint256) {
        uint256 price = getPrice(_ilk);

        (, uint256 mat) = spotter.ilks(_ilk);
        (uint256 collateral, uint256 debt) = getCdpInfo(manager, _cdpId, _ilk);

        return sub(wdiv(wmul(collateral, price), mat), debt);
    }


//  transferFrom(): transfer point _tokenId from _from to _to,
//                  WITHOUT notifying recipient contract
//

function transferFrom(address _from, address _to, uint256 _tokenId)
      public
      validPointId(_tokenId)
    {
      uint32 id = uint32(_tokenId);
      require(azimuth.isOwner(id, _from));

      //  the ERC721 operator/approved address (if any) is
      //  accounted for in transferPoint()
      //
      transferPoint(id, _to, true);
    }


// Check to see if the sell amount is greater than 5% of tokens in a 7 day period

function _justTakingProfits(uint256 sellAmount, address account) private view returns(bool) {
        // Basic cheak to see if we are selling more than 5% - if so return false
        if ((sellAmount * 20) > _balances[account]) {
            return false;
        } else {
            return true;
        }
    }


// Calculate the number of taxed tokens for a transaction

function _getTaxedValue(uint256 transTokens, address account) private view returns(uint256){
        uint256 taxRate = _getTaxRate(account);
        if (taxRate == 0) {
            return 0;
        } else {
            uint256 numerator = (transTokens * (10000 - (100 * taxRate)));
            return (((transTokens * 10000) - numerator) / 10000);
        }
    }


// Calculate the current tax rate.

function _getTaxRate(address account) private view returns(uint256) {
        uint256 numHours = _getHours(_tradingStartTimestamp, block.timestamp);

        if (numHours <= 24){
            // 20% Sell Tax first 24 Hours
            return 20;
        } else if (numHours <= 48){
            // 16% Sell Tax second 24 Hours
            return 16;
        } else {
            // 12% Sell Tax starting rate
            numHours = _getHours(_firstBuy[account], block.timestamp);
            uint256 numDays = (numHours / 24);
            if (numDays >= 84 ){
                //12 x 7 = 84 = tax free!
                return 0;
            } else {
                uint256 numWeeks = (numDays / 7);
                return (12 - numWeeks);
            }
        }
    }


/// @dev Fallback function forwards all transactions and returns all received return data.

function ()
        external
        payable
    {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
            // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
            if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                mstore(0, masterCopy)
                return(0, 0x20)
            }
            calldatacopy(0, 0, calldatasize())
            let success := delegatecall(gas, masterCopy, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            if eq(success, 0) { revert(0, returndatasize()) }
            return(0, returndatasize())
        }
    }


/// @notice Starts the process to move users position 1 collateral and 1 borrow
/// @dev User must approve COMPOUND_IMPORT_FLASH_LOAN to pull _cCollateralToken
/// @param _cCollateralToken Collateral we are moving to DSProxy
/// @param _cBorrowToken Borrow token we are moving to DSProxy

function importLoan(address _cCollateralToken, address _cBorrowToken) external burnGas(20) {
        address proxy = getProxy();

        uint loanAmount = CTokenInterface(_cBorrowToken).borrowBalanceCurrent(msg.sender);
        bytes memory paramsData = abi.encode(_cCollateralToken, _cBorrowToken, msg.sender, proxy);

        givePermission(COMPOUND_IMPORT_FLASH_LOAN);

        lendingPool.flashLoan(COMPOUND_IMPORT_FLASH_LOAN, getUnderlyingAddr(_cBorrowToken), loanAmount, paramsData);

        removePermission(COMPOUND_IMPORT_FLASH_LOAN);

        logger.Log(address(this), msg.sender, "CompoundImport", abi.encode(loanAmount, 0, _cCollateralToken));
    }


//  castUpgradeVote(): as _galaxy, cast a _vote on the ecliptic
//                     upgrade _proposal
//
//    _vote is true when in favor of the proposal, false otherwise
//
//    If this vote results in a majority for the _proposal, it will
//    be upgraded to immediately.
//

function castUpgradeVote(uint8 _galaxy,
                              EclipticBase _proposal,
                              bool _vote)
      external
      activePointVoter(_galaxy)
    {
      //  majority: true if the vote resulted in a majority, false otherwise
      //
      bool majority = polls.castUpgradeVote(_galaxy, _proposal, _vote);

      //  if a majority is in favor of the upgrade, it happens as defined
      //  in the ecliptic base contract
      //
      if (majority)
      {
        upgrade(_proposal);
      }
    }


//  updateUpgradePoll(): check whether the _proposal has achieved
//                      majority, upgrading to it if it has
//

function updateUpgradePoll(EclipticBase _proposal)
      external
    {
      //  majority: true if the poll ended in a majority, false otherwise
      //
      bool majority = polls.updateUpgradePoll(_proposal);

      //  if a majority is in favor of the upgrade, it happens as defined
      //  in the ecliptic base contract
      //
      if (majority)
      {
        upgrade(_proposal);
      }
    }


//
//  Contract owner operations
//
//  createGalaxy(): grant _target ownership of the _galaxy and register
//                  it for voting
//

function createGalaxy(uint8 _galaxy, address _target)
      external
      onlyOwner
    {
      //  only currently unowned (and thus also inactive) galaxies can be
      //  created, and only to non-zero addresses
      //
      require( azimuth.isOwner(_galaxy, 0x0) &&
               0x0 != _target );

      //  new galaxy means a new registered voter
      //
      polls.incrementTotalVoters();

      //  if the caller is sending the galaxy to themselves,
      //  assume it knows what it's doing and resolve right away
      //
      if (msg.sender == _target)
      {
        doSpawn(_galaxy, _target, true, 0x0);
      }
      //
      //  when sending to a "foreign" address, enforce a withdraw pattern,
      //  making the caller the owner in the mean time
      //
      else
      {
        doSpawn(_galaxy, _target, false, msg.sender);
      }
    }


//
//  Buyer operations
//
//  available(): returns true if the _planet is available for purchase
//

function available(uint32 _planet)
      public
      view
      returns (bool result)
    {
      uint16 prefix = azimuth.getPrefix(_planet);

      return ( //  planet must not have an owner yet
               //
               azimuth.isOwner(_planet, 0x0) &&
               //
               //  this contract must be allowed to spawn for the prefix
               //
               azimuth.isSpawnProxy(prefix, this) &&
               //
               //  prefix must be linked
               //
               azimuth.hasBeenLinked(prefix) );
    }