ETH Price: $3,268.93 (-1.26%)
Gas: 10 Gwei

Contract

0xC09347297F274807415EB17364C77B52F9F95B9F
 

Overview

ETH Balance

0.085500000000000196 ETH

Eth Value

$279.49 (@ $3,268.93/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Finish PVP79873962019-06-19 7:27:261868 days ago1560929246IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79873962019-06-19 7:27:261868 days ago1560929246IN
0xC0934729...2F9F95B9F
0 ETH0.000626035
Finish PVP79342002019-06-10 23:50:391876 days ago1560210639IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79341712019-06-10 23:44:251876 days ago1560210265IN
0xC0934729...2F9F95B9F
0 ETH0.000701475.62505
Finish PVP79323132019-06-10 16:43:141877 days ago1560184994IN
0xC0934729...2F9F95B9F
0 ETH0.0033258820
Finish PVP79300492019-06-10 8:13:121877 days ago1560154392IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79300482019-06-10 8:12:531877 days ago1560154373IN
0xC0934729...2F9F95B9F
0 ETH0.0027356523.12080882
Finish PVP79294992019-06-10 6:09:281877 days ago1560146968IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79294982019-06-10 6:08:471877 days ago1560146927IN
0xC0934729...2F9F95B9F
0 ETH0.0030808126.01113863
Finish PVP79273962019-06-09 22:29:281877 days ago1560119368IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79273962019-06-09 22:29:281877 days ago1560119368IN
0xC0934729...2F9F95B9F
0 ETH0.000783715
Finish Tournamen...79119662019-06-07 12:10:111880 days ago1559909411IN
0xC0934729...2F9F95B9F
0 ETH0.000112575
Finish Tournamen...79119662019-06-07 12:10:111880 days ago1559909411IN
0xC0934729...2F9F95B9F
0 ETH0.0002567611.40419629
Finish PVP79062012019-06-06 14:28:041881 days ago1559831284IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79062012019-06-06 14:28:041881 days ago1559831284IN
0xC0934729...2F9F95B9F
0 ETH0.000842015.62505
Finish PVP79010412019-06-05 19:22:491882 days ago1559762569IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP79010392019-06-05 19:22:111882 days ago1559762531IN
0xC0934729...2F9F95B9F
0 ETH0.0036996926.01114264
Finish PVP78991712019-06-05 12:13:371882 days ago1559736817IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP78991692019-06-05 12:13:111882 days ago1559736791IN
0xC0934729...2F9F95B9F
0 ETH0.0015218310.13697326
Finish PVP78866872019-06-03 13:23:051884 days ago1559568185IN
0xC0934729...2F9F95B9F
0 ETH0.000112155
Finish PVP78866872019-06-03 13:23:051884 days ago1559568185IN
0xC0934729...2F9F95B9F
0 ETH0.000625115
Finish PVP78647552019-05-31 3:02:351887 days ago1559271755IN
0xC0934729...2F9F95B9F
0 ETH0.000622045
Finish PVP78326492019-05-26 2:35:291892 days ago1558838129IN
0xC0934729...2F9F95B9F
0 ETH0.0002579411.50000025
Finish PVP78326472019-05-26 2:35:131892 days ago1558838113IN
0xC0934729...2F9F95B9F
0 ETH0.000622045
Remove PVP Conte...78037612019-05-21 14:23:501897 days ago1558448630IN
0xC0934729...2F9F95B9F
0 ETH0.000391611.50000025
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
79873962019-06-19 7:27:261868 days ago1560929246
0xC0934729...2F9F95B9F
0.0015 ETH
79873962019-06-19 7:27:261868 days ago1560929246
0xC0934729...2F9F95B9F
0.0015 ETH
79873962019-06-19 7:27:261868 days ago1560929246
0xC0934729...2F9F95B9F
0.0225 ETH
79873322019-06-19 7:14:541868 days ago1560928494
0xC0934729...2F9F95B9F
0.01 ETH
79415852019-06-12 3:34:491875 days ago1560310489
0xC0934729...2F9F95B9F
0.02 ETH
79341712019-06-10 23:44:251876 days ago1560210265
0xC0934729...2F9F95B9F
0.0055 ETH
79341712019-06-10 23:44:251876 days ago1560210265
0xC0934729...2F9F95B9F
0.0055 ETH
79341712019-06-10 23:44:251876 days ago1560210265
0xC0934729...2F9F95B9F
0.0825 ETH
79323132019-06-10 16:43:141877 days ago1560184994
0xC0934729...2F9F95B9F
0.002 ETH
79323132019-06-10 16:43:141877 days ago1560184994
0xC0934729...2F9F95B9F
0.002 ETH
79323132019-06-10 16:43:141877 days ago1560184994
0xC0934729...2F9F95B9F
0.03 ETH
79314762019-06-10 13:23:361877 days ago1560173016
0xC0934729...2F9F95B9F
0.04 ETH
79314742019-06-10 13:23:321877 days ago1560173012
0xC0934729...2F9F95B9F
0.03 ETH
79308032019-06-10 11:01:231877 days ago1560164483
0xC0934729...2F9F95B9F
0.07 ETH
79308012019-06-10 11:00:521877 days ago1560164452
0xC0934729...2F9F95B9F
0.01 ETH
79300482019-06-10 8:12:531877 days ago1560154373
0xC0934729...2F9F95B9F
0.003 ETH
79300482019-06-10 8:12:531877 days ago1560154373
0xC0934729...2F9F95B9F
0.003 ETH
79300482019-06-10 8:12:531877 days ago1560154373
0xC0934729...2F9F95B9F
0.045 ETH
79299872019-06-10 7:57:491877 days ago1560153469
0xC0934729...2F9F95B9F
0.03 ETH
79299852019-06-10 7:57:261877 days ago1560153446
0xC0934729...2F9F95B9F
0.03 ETH
79294982019-06-10 6:08:471877 days ago1560146927
0xC0934729...2F9F95B9F
0.0045 ETH
79294982019-06-10 6:08:471877 days ago1560146927
0xC0934729...2F9F95B9F
0.0045 ETH
79294982019-06-10 6:08:471877 days ago1560146927
0xC0934729...2F9F95B9F
0.0675 ETH
79273962019-06-09 22:29:281877 days ago1560119368
0xC0934729...2F9F95B9F
0.0015 ETH
79273962019-06-09 22:29:281877 days ago1560119368
0xC0934729...2F9F95B9F
0.0015 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BattleProvider

Compiler Version
v0.4.19+commit.c4cbbb05

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2018-04-20
*/

pragma solidity ^0.4.19;

contract ERC721 {
    // Required methods
    function totalSupply() public view returns (uint256 total);
    function balanceOf(address _owner) public view returns (uint256 balance);
    function ownerOf(uint256 _tokenId) external view returns (address owner);
    function approve(address _to, uint256 _tokenId) external;
    function transfer(address _to, uint256 _tokenId) external;
    function transferFrom(address _from, address _to, uint256 _tokenId) external;

    // Events
    event Transfer(address from, address to, uint256 tokenId);
    event Approval(address owner, address approved, uint256 tokenId);

    // Optional
    // function name() public view returns (string name);
    // function symbol() public view returns (string symbol);
    // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
    // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);
    // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
    function getBeneficiary() external view returns(address);
}

contract SanctuaryInterface {
    /// @dev simply a boolean to indicate this is the contract we expect to be
    function isSanctuary() public pure returns (bool);

    /// @dev generate new warrior genes
    /// @param _heroGenes Genes of warrior that have completed dungeon
    /// @param _heroLevel Level of the warrior
    /// @return the genes that are supposed to be passed down to newly arisen warrior
    function generateWarrior(uint256 _heroGenes, uint256 _heroLevel, uint256 _targetBlock, uint256 _perkId) public returns (uint256);
}

contract PVPInterface {
    /// @dev simply a boolean to indicate this is the contract we expect to be
    function isPVPProvider() external pure returns (bool);
    
    function addTournamentContender(address _owner, uint256[] _tournamentData) external payable;
    function getTournamentThresholdFee() public view returns(uint256);
    
    function addPVPContender(address _owner, uint256 _packedWarrior) external payable;
    function getPVPEntranceFee(uint256 _levelPoints) external view returns(uint256);
}

contract PVPListenerInterface {
    /// @dev simply a boolean to indicate this is the contract we expect to be
    function isPVPListener() public pure returns (bool);
    function getBeneficiary() external view returns(address);
    
    function pvpFinished(uint256[] warriorData, uint256 matchingCount) public;
    function pvpContenderRemoved(uint256 _warriorId) public;
    function tournamentFinished(uint256[] packedContenders) public;
}

contract PermissionControll {
    // This facet controls access to contract that implements it. There are four roles managed here:
    //
    // - The Admin: The Admin can reassign admin and issuer roles and change the addresses of our dependent smart
    // contracts. It is also the only role that can unpause the smart contract. It is initially
    // set to the address that created the smart contract in the CryptoWarriorCore constructor.
    //
    // - The Bank: The Bank can withdraw funds from CryptoWarriorCore and its auction and battle contracts, and change admin role.
    //
    // - The Issuer: The Issuer can release gen0 warriors to auction.
    //
    // / @dev Emited when contract is upgraded
    event ContractUpgrade(address newContract);
    
    // Set in case the core contract is broken and an upgrade is required
    address public newContractAddress;

    // The addresses of the accounts (or contracts) that can execute actions within each roles.
    address public adminAddress;
    address public bankAddress;
    address public issuerAddress; 
    // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
    bool public paused = false;
    

    // / @dev Access modifier for Admin-only functionality
    modifier onlyAdmin(){
        require(msg.sender == adminAddress);
        _;
    }

    // / @dev Access modifier for Bank-only functionality
    modifier onlyBank(){
        require(msg.sender == bankAddress);
        _;
    }
    
    /// @dev Access modifier for Issuer-only functionality
    modifier onlyIssuer(){
    		require(msg.sender == issuerAddress);
        _;
    }
    
    modifier onlyAuthorized(){
        require(msg.sender == issuerAddress ||
            msg.sender == adminAddress ||
            msg.sender == bankAddress);
        _;
    }


    // / @dev Assigns a new address to act as the Bank. Only available to the current Bank.
    // / @param _newBank The address of the new Bank
    function setBank(address _newBank) external onlyBank {
        require(_newBank != address(0));
        bankAddress = _newBank;
    }

    // / @dev Assigns a new address to act as the Admin. Only available to the current Admin.
    // / @param _newAdmin The address of the new Admin
    function setAdmin(address _newAdmin) external {
        require(msg.sender == adminAddress || msg.sender == bankAddress);
        require(_newAdmin != address(0));
        adminAddress = _newAdmin;
    }
    
    // / @dev Assigns a new address to act as the Issuer. Only available to the current Issuer.
    // / @param _newIssuer The address of the new Issuer
    function setIssuer(address _newIssuer) external onlyAdmin{
        require(_newIssuer != address(0));
        issuerAddress = _newIssuer;
    }

    /*** Pausable functionality adapted from OpenZeppelin ***/
    // / @dev Modifier to allow actions only when the contract IS NOT paused
    modifier whenNotPaused(){
        require(!paused);
        _;
    }

    // / @dev Modifier to allow actions only when the contract IS paused
    modifier whenPaused{
        require(paused);
        _;
    }

    // / @dev Called by any "Authorized" role to pause the contract. Used only when
    // /  a bug or exploit is detected and we need to limit damage.
    function pause() external onlyAuthorized whenNotPaused{
        paused = true;
    }

    // / @dev Unpauses the smart contract. Can only be called by the Admin.
    // / @notice This is public rather than external so it can be called by
    // /  derived contracts.
    function unpause() public onlyAdmin whenPaused{
        // can't unpause if contract was upgraded
        paused = false;
    }
    
    
    /// @dev Used to mark the smart contract as upgraded, in case there is a serious
    ///  breaking bug. This method does nothing but keep track of the new contract and
    ///  emit a message indicating that the new address is set. It's up to clients of this
    ///  contract to update to the new contract address in that case. (This contract will
    ///  be paused indefinitely if such an upgrade takes place.)
    /// @param _v2Address new address
    function setNewAddress(address _v2Address) external onlyAdmin whenPaused {
        newContractAddress = _v2Address;
        ContractUpgrade(_v2Address);
    }
}

contract Ownable {
    address public owner;

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    function Ownable() public{
        owner = msg.sender;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner{
        if (newOwner != address(0)) {
            owner = newOwner;
        }
    }
}

contract PausableBattle is Ownable {
    event PausePVP(bool paused);
    event PauseTournament(bool paused);
    
    bool public pvpPaused = false;
    bool public tournamentPaused = false;
    
    /** PVP */
    modifier PVPNotPaused(){
        require(!pvpPaused);
        _;
    }

    modifier PVPPaused{
        require(pvpPaused);
        _;
    }

    function pausePVP() public onlyOwner PVPNotPaused {
        pvpPaused = true;
        PausePVP(true);
    }

    function unpausePVP() public onlyOwner PVPPaused {
        pvpPaused = false;
        PausePVP(false);
    }
    
    /** Tournament */
    modifier TournamentNotPaused(){
        require(!tournamentPaused);
        _;
    }

    modifier TournamentPaused{
        require(tournamentPaused);
        _;
    }

    function pauseTournament() public onlyOwner TournamentNotPaused {
        tournamentPaused = true;
        PauseTournament(true);
    }

    function unpauseTournament() public onlyOwner TournamentPaused {
        tournamentPaused = false;
        PauseTournament(false);
    }
    
}

contract Pausable is Ownable {
    event Pause();

    event Unpause();

    bool public paused = false;

    /**
     * @dev modifier to allow actions only when the contract IS paused
     */
    modifier whenNotPaused(){
        require(!paused);
        _;
    }

    /**
     * @dev modifier to allow actions only when the contract IS NOT paused
     */
    modifier whenPaused{
        require(paused);
        _;
    }

    /**
     * @dev called by the owner to pause, triggers stopped state
     */
    function pause() public onlyOwner whenNotPaused {
        paused = true;
        Pause();
    }

    /**
     * @dev called by the owner to unpause, returns to normal state
     */
    function unpause() public onlyOwner whenPaused {
        paused = false;
        Unpause();
    }
}

library CryptoUtils {
   
    /* CLASSES */
    uint256 internal constant WARRIOR = 0;
    uint256 internal constant ARCHER = 1;
    uint256 internal constant MAGE = 2;
    /* RARITIES */
    uint256 internal constant COMMON = 1;
    uint256 internal constant UNCOMMON = 2;
    uint256 internal constant RARE = 3;
    uint256 internal constant MYTHIC = 4;
    uint256 internal constant LEGENDARY = 5;
    uint256 internal constant UNIQUE = 6;
    /* LIMITS */
    uint256 internal constant CLASS_MECHANICS_MAX = 3;
    uint256 internal constant RARITY_MAX = 6;
    /*@dev range used for rarity chance computation */
    uint256 internal constant RARITY_CHANCE_RANGE = 10000000;
    uint256 internal constant POINTS_TO_LEVEL = 10;
    /* ATTRIBUTE MASKS */
    /*@dev range 0-9999 */
    uint256 internal constant UNIQUE_MASK_0 = 1;
    /*@dev range 0-9 */
    uint256 internal constant RARITY_MASK_1 = UNIQUE_MASK_0 * 10000;
    /*@dev range 0-999 */
    uint256 internal constant CLASS_VIEW_MASK_2 = RARITY_MASK_1 * 10;
    /*@dev range 0-999 */
    uint256 internal constant BODY_COLOR_MASK_3 = CLASS_VIEW_MASK_2 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant EYES_MASK_4 = BODY_COLOR_MASK_3 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant MOUTH_MASK_5 = EYES_MASK_4 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant HEIR_MASK_6 = MOUTH_MASK_5 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant HEIR_COLOR_MASK_7 = HEIR_MASK_6 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant ARMOR_MASK_8 = HEIR_COLOR_MASK_7 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant WEAPON_MASK_9 = ARMOR_MASK_8 * 1000;
    /*@dev range 0-999 */
    uint256 internal constant HAT_MASK_10 = WEAPON_MASK_9 * 1000;
    /*@dev range 0-99 */
    uint256 internal constant RUNES_MASK_11 = HAT_MASK_10 * 1000;
    /*@dev range 0-99 */
    uint256 internal constant WINGS_MASK_12 = RUNES_MASK_11 * 100;
    /*@dev range 0-99 */
    uint256 internal constant PET_MASK_13 = WINGS_MASK_12 * 100;
    /*@dev range 0-99 */
    uint256 internal constant BORDER_MASK_14 = PET_MASK_13 * 100;
    /*@dev range 0-99 */
    uint256 internal constant BACKGROUND_MASK_15 = BORDER_MASK_14 * 100;
    /*@dev range 0-99 */
    uint256 internal constant INTELLIGENCE_MASK_16 = BACKGROUND_MASK_15 * 100;
    /*@dev range 0-99 */
    uint256 internal constant AGILITY_MASK_17 = INTELLIGENCE_MASK_16 * 100;
    /*@dev range 0-99 */
    uint256 internal constant STRENGTH_MASK_18 = AGILITY_MASK_17 * 100;
    /*@dev range 0-9 */
    uint256 internal constant CLASS_MECH_MASK_19 = STRENGTH_MASK_18 * 100;
    /*@dev range 0-999 */
    uint256 internal constant RARITY_BONUS_MASK_20 = CLASS_MECH_MASK_19 * 10;
    /*@dev range 0-9 */
    uint256 internal constant SPECIALITY_MASK_21 = RARITY_BONUS_MASK_20 * 1000;
    /*@dev range 0-99 */
    uint256 internal constant DAMAGE_MASK_22 = SPECIALITY_MASK_21 * 10;
    /*@dev range 0-99 */
    uint256 internal constant AURA_MASK_23 = DAMAGE_MASK_22 * 100;
    /*@dev 20 decimals left */
    uint256 internal constant BASE_MASK_24 = AURA_MASK_23 * 100;
    
    
    /* SPECIAL PERKS */
    uint256 internal constant MINER_PERK = 1;
    
    
    /* PARAM INDEXES */
    uint256 internal constant BODY_COLOR_MAX_INDEX_0 = 0;
    uint256 internal constant EYES_MAX_INDEX_1 = 1;
    uint256 internal constant MOUTH_MAX_2 = 2;
    uint256 internal constant HAIR_MAX_3 = 3;
    uint256 internal constant HEIR_COLOR_MAX_4 = 4;
    uint256 internal constant ARMOR_MAX_5 = 5;
    uint256 internal constant WEAPON_MAX_6 = 6;
    uint256 internal constant HAT_MAX_7 = 7;
    uint256 internal constant RUNES_MAX_8 = 8;
    uint256 internal constant WINGS_MAX_9 = 9;
    uint256 internal constant PET_MAX_10 = 10;
    uint256 internal constant BORDER_MAX_11 = 11;
    uint256 internal constant BACKGROUND_MAX_12 = 12;
    uint256 internal constant UNIQUE_INDEX_13 = 13;
    uint256 internal constant LEGENDARY_INDEX_14 = 14;
    uint256 internal constant MYTHIC_INDEX_15 = 15;
    uint256 internal constant RARE_INDEX_16 = 16;
    uint256 internal constant UNCOMMON_INDEX_17 = 17;
    uint256 internal constant UNIQUE_TOTAL_INDEX_18 = 18;
    
     /* PACK PVP DATA LOGIC */
    //pvp data
    uint256 internal constant CLASS_PACK_0 = 1;
    uint256 internal constant RARITY_BONUS_PACK_1 = CLASS_PACK_0 * 10;
    uint256 internal constant RARITY_PACK_2 = RARITY_BONUS_PACK_1 * 1000;
    uint256 internal constant EXPERIENCE_PACK_3 = RARITY_PACK_2 * 10;
    uint256 internal constant INTELLIGENCE_PACK_4 = EXPERIENCE_PACK_3 * 1000;
    uint256 internal constant AGILITY_PACK_5 = INTELLIGENCE_PACK_4 * 100;
    uint256 internal constant STRENGTH_PACK_6 = AGILITY_PACK_5 * 100;
    uint256 internal constant BASE_DAMAGE_PACK_7 = STRENGTH_PACK_6 * 100;
    uint256 internal constant PET_PACK_8 = BASE_DAMAGE_PACK_7 * 100;
    uint256 internal constant AURA_PACK_9 = PET_PACK_8 * 100;
    uint256 internal constant WARRIOR_ID_PACK_10 = AURA_PACK_9 * 100;
    uint256 internal constant PVP_CYCLE_PACK_11 = WARRIOR_ID_PACK_10 * 10**10;
    uint256 internal constant RATING_PACK_12 = PVP_CYCLE_PACK_11 * 10**10;
    uint256 internal constant PVP_BASE_PACK_13 = RATING_PACK_12 * 10**10;//NB rating must be at the END!
    
    //tournament data
    uint256 internal constant HP_PACK_0 = 1;
    uint256 internal constant DAMAGE_PACK_1 = HP_PACK_0 * 10**12;
    uint256 internal constant ARMOR_PACK_2 = DAMAGE_PACK_1 * 10**12;
    uint256 internal constant DODGE_PACK_3 = ARMOR_PACK_2 * 10**12;
    uint256 internal constant PENETRATION_PACK_4 = DODGE_PACK_3 * 10**12;
    uint256 internal constant COMBINE_BASE_PACK_5 = PENETRATION_PACK_4 * 10**12;
    
    /* MISC CONSTANTS */
    uint256 internal constant MAX_ID_SIZE = 10000000000;
    int256 internal constant PRECISION = 1000000;
    
    uint256 internal constant BATTLES_PER_CONTENDER = 10;//10x100
    uint256 internal constant BATTLES_PER_CONTENDER_SUM = BATTLES_PER_CONTENDER * 100;//10x100
    
    uint256 internal constant LEVEL_BONUSES = 98898174676155504541373431282523211917151413121110;
    
    //ucommon bonuses
    uint256 internal constant BONUS_NONE = 0;
    uint256 internal constant BONUS_HP = 1;
    uint256 internal constant BONUS_ARMOR = 2;
    uint256 internal constant BONUS_CRIT_CHANCE = 3;
    uint256 internal constant BONUS_CRIT_MULT = 4;
    uint256 internal constant BONUS_PENETRATION = 5;
    //rare bonuses
    uint256 internal constant BONUS_STR = 6;
    uint256 internal constant BONUS_AGI = 7;
    uint256 internal constant BONUS_INT = 8;
    uint256 internal constant BONUS_DAMAGE = 9;
    
    //bonus value database, 
    uint256 internal constant BONUS_DATA = 16060606140107152000;
    //pets database
    uint256 internal constant PETS_DATA = 287164235573728325842459981692000;
    
    uint256 internal constant PET_AURA = 2;
    uint256 internal constant PET_PARAM_1 = 1;
    uint256 internal constant PET_PARAM_2 = 0;

    /* GETTERS */
	function getUniqueValue(uint256 identity) internal pure returns(uint256){
		return identity % RARITY_MASK_1;
	}

    function getRarityValue(uint256 identity) internal pure returns(uint256){
        return (identity % CLASS_VIEW_MASK_2) / RARITY_MASK_1;
    }

	function getClassViewValue(uint256 identity) internal pure returns(uint256){
		return (identity % BODY_COLOR_MASK_3) / CLASS_VIEW_MASK_2;
	}

	function getBodyColorValue(uint256 identity) internal pure returns(uint256){
        return (identity % EYES_MASK_4) / BODY_COLOR_MASK_3;
    }

    function getEyesValue(uint256 identity) internal pure returns(uint256){
        return (identity % MOUTH_MASK_5) / EYES_MASK_4;
    }

    function getMouthValue(uint256 identity) internal pure returns(uint256){
        return (identity % HEIR_MASK_6) / MOUTH_MASK_5;
    }

    function getHairValue(uint256 identity) internal pure returns(uint256){
        return (identity % HEIR_COLOR_MASK_7) / HEIR_MASK_6;
    }

    function getHairColorValue(uint256 identity) internal pure returns(uint256){
        return (identity % ARMOR_MASK_8) / HEIR_COLOR_MASK_7;
    }

    function getArmorValue(uint256 identity) internal pure returns(uint256){
        return (identity % WEAPON_MASK_9) / ARMOR_MASK_8;
    }

    function getWeaponValue(uint256 identity) internal pure returns(uint256){
        return (identity % HAT_MASK_10) / WEAPON_MASK_9;
    }

    function getHatValue(uint256 identity) internal pure returns(uint256){
        return (identity % RUNES_MASK_11) / HAT_MASK_10;
    }

    function getRunesValue(uint256 identity) internal pure returns(uint256){
        return (identity % WINGS_MASK_12) / RUNES_MASK_11;
    }

    function getWingsValue(uint256 identity) internal pure returns(uint256){
        return (identity % PET_MASK_13) / WINGS_MASK_12;
    }

    function getPetValue(uint256 identity) internal pure returns(uint256){
        return (identity % BORDER_MASK_14) / PET_MASK_13;
    }

	function getBorderValue(uint256 identity) internal pure returns(uint256){
		return (identity % BACKGROUND_MASK_15) / BORDER_MASK_14;
	}

	function getBackgroundValue(uint256 identity) internal pure returns(uint256){
		return (identity % INTELLIGENCE_MASK_16) / BACKGROUND_MASK_15;
	}

    function getIntelligenceValue(uint256 identity) internal pure returns(uint256){
        return (identity % AGILITY_MASK_17) / INTELLIGENCE_MASK_16;
    }

    function getAgilityValue(uint256 identity) internal pure returns(uint256){
        return ((identity % STRENGTH_MASK_18) / AGILITY_MASK_17);
    }

    function getStrengthValue(uint256 identity) internal pure returns(uint256){
        return ((identity % CLASS_MECH_MASK_19) / STRENGTH_MASK_18);
    }

    function getClassMechValue(uint256 identity) internal pure returns(uint256){
        return (identity % RARITY_BONUS_MASK_20) / CLASS_MECH_MASK_19;
    }

    function getRarityBonusValue(uint256 identity) internal pure returns(uint256){
        return (identity % SPECIALITY_MASK_21) / RARITY_BONUS_MASK_20;
    }

    function getSpecialityValue(uint256 identity) internal pure returns(uint256){
        return (identity % DAMAGE_MASK_22) / SPECIALITY_MASK_21;
    }
    
    function getDamageValue(uint256 identity) internal pure returns(uint256){
        return (identity % AURA_MASK_23) / DAMAGE_MASK_22;
    }

    function getAuraValue(uint256 identity) internal pure returns(uint256){
        return ((identity % BASE_MASK_24) / AURA_MASK_23);
    }

    /* SETTERS */
    function _setUniqueValue0(uint256 value) internal pure returns(uint256){
        require(value < RARITY_MASK_1);
        return value * UNIQUE_MASK_0;
    }

    function _setRarityValue1(uint256 value) internal pure returns(uint256){
        require(value < (CLASS_VIEW_MASK_2 / RARITY_MASK_1));
        return value * RARITY_MASK_1;
    }

    function _setClassViewValue2(uint256 value) internal pure returns(uint256){
        require(value < (BODY_COLOR_MASK_3 / CLASS_VIEW_MASK_2));
        return value * CLASS_VIEW_MASK_2;
    }

    function _setBodyColorValue3(uint256 value) internal pure returns(uint256){
        require(value < (EYES_MASK_4 / BODY_COLOR_MASK_3));
        return value * BODY_COLOR_MASK_3;
    }

    function _setEyesValue4(uint256 value) internal pure returns(uint256){
        require(value < (MOUTH_MASK_5 / EYES_MASK_4));
        return value * EYES_MASK_4;
    }

    function _setMouthValue5(uint256 value) internal pure returns(uint256){
        require(value < (HEIR_MASK_6 / MOUTH_MASK_5));
        return value * MOUTH_MASK_5;
    }

    function _setHairValue6(uint256 value) internal pure returns(uint256){
        require(value < (HEIR_COLOR_MASK_7 / HEIR_MASK_6));
        return value * HEIR_MASK_6;
    }

    function _setHairColorValue7(uint256 value) internal pure returns(uint256){
        require(value < (ARMOR_MASK_8 / HEIR_COLOR_MASK_7));
        return value * HEIR_COLOR_MASK_7;
    }

    function _setArmorValue8(uint256 value) internal pure returns(uint256){
        require(value < (WEAPON_MASK_9 / ARMOR_MASK_8));
        return value * ARMOR_MASK_8;
    }

    function _setWeaponValue9(uint256 value) internal pure returns(uint256){
        require(value < (HAT_MASK_10 / WEAPON_MASK_9));
        return value * WEAPON_MASK_9;
    }

    function _setHatValue10(uint256 value) internal pure returns(uint256){
        require(value < (RUNES_MASK_11 / HAT_MASK_10));
        return value * HAT_MASK_10;
    }

    function _setRunesValue11(uint256 value) internal pure returns(uint256){
        require(value < (WINGS_MASK_12 / RUNES_MASK_11));
        return value * RUNES_MASK_11;
    }

    function _setWingsValue12(uint256 value) internal pure returns(uint256){
        require(value < (PET_MASK_13 / WINGS_MASK_12));
        return value * WINGS_MASK_12;
    }

    function _setPetValue13(uint256 value) internal pure returns(uint256){
        require(value < (BORDER_MASK_14 / PET_MASK_13));
        return value * PET_MASK_13;
    }

    function _setBorderValue14(uint256 value) internal pure returns(uint256){
        require(value < (BACKGROUND_MASK_15 / BORDER_MASK_14));
        return value * BORDER_MASK_14;
    }

    function _setBackgroundValue15(uint256 value) internal pure returns(uint256){
        require(value < (INTELLIGENCE_MASK_16 / BACKGROUND_MASK_15));
        return value * BACKGROUND_MASK_15;
    }

    function _setIntelligenceValue16(uint256 value) internal pure returns(uint256){
        require(value < (AGILITY_MASK_17 / INTELLIGENCE_MASK_16));
        return value * INTELLIGENCE_MASK_16;
    }

    function _setAgilityValue17(uint256 value) internal pure returns(uint256){
        require(value < (STRENGTH_MASK_18 / AGILITY_MASK_17));
        return value * AGILITY_MASK_17;
    }

    function _setStrengthValue18(uint256 value) internal pure returns(uint256){
        require(value < (CLASS_MECH_MASK_19 / STRENGTH_MASK_18));
        return value * STRENGTH_MASK_18;
    }

    function _setClassMechValue19(uint256 value) internal pure returns(uint256){
        require(value < (RARITY_BONUS_MASK_20 / CLASS_MECH_MASK_19));
        return value * CLASS_MECH_MASK_19;
    }

    function _setRarityBonusValue20(uint256 value) internal pure returns(uint256){
        require(value < (SPECIALITY_MASK_21 / RARITY_BONUS_MASK_20));
        return value * RARITY_BONUS_MASK_20;
    }

    function _setSpecialityValue21(uint256 value) internal pure returns(uint256){
        require(value < (DAMAGE_MASK_22 / SPECIALITY_MASK_21));
        return value * SPECIALITY_MASK_21;
    }
    
    function _setDamgeValue22(uint256 value) internal pure returns(uint256){
        require(value < (AURA_MASK_23 / DAMAGE_MASK_22));
        return value * DAMAGE_MASK_22;
    }

    function _setAuraValue23(uint256 value) internal pure returns(uint256){
        require(value < (BASE_MASK_24 / AURA_MASK_23));
        return value * AURA_MASK_23;
    }
    
    /* WARRIOR IDENTITY GENERATION */
    function _computeRunes(uint256 _rarity) internal pure returns (uint256){
        return _rarity > UNCOMMON ? _rarity - UNCOMMON : 0;// 1 + _random(0, max, hash, WINGS_MASK_12, RUNES_MASK_11) : 0;
    }

    function _computeWings(uint256 _rarity, uint256 max, uint256 hash) internal pure returns (uint256){
        return _rarity > RARE ?  1 + _random(0, max, hash, PET_MASK_13, WINGS_MASK_12) : 0;
    }

    function _computePet(uint256 _rarity, uint256 max, uint256 hash) internal pure returns (uint256){
        return _rarity > MYTHIC ? 1 + _random(0, max, hash, BORDER_MASK_14, PET_MASK_13) : 0;
    }

    function _computeBorder(uint256 _rarity) internal pure returns (uint256){
        return _rarity >= COMMON ? _rarity - 1 : 0;
    }

    function _computeBackground(uint256 _rarity) internal pure returns (uint256){
        return _rarity;
    }
    
    function _unpackPetData(uint256 index) internal pure returns(uint256){
        return (PETS_DATA % (1000 ** (index + 1)) / (1000 ** index));
    }
    
    function _getPetBonus1(uint256 _pet) internal pure returns(uint256) {
        return (_pet % (10 ** (PET_PARAM_1 + 1)) / (10 ** PET_PARAM_1));
    }
    
    function _getPetBonus2(uint256 _pet) internal pure returns(uint256) {
        return (_pet % (10 ** (PET_PARAM_2 + 1)) / (10 ** PET_PARAM_2));
    }
    
    function _getPetAura(uint256 _pet) internal pure returns(uint256) {
        return (_pet % (10 ** (PET_AURA + 1)) / (10 ** PET_AURA));
    }
    
    function _getBattleBonus(uint256 _setBonusIndex, uint256 _currentBonusIndex, uint256 _petData, uint256 _warriorAuras, uint256 _petAuras) internal pure returns(int256) {
        int256 bonus = 0;
        if (_setBonusIndex == _currentBonusIndex) {
            bonus += int256(BONUS_DATA % (100 ** (_setBonusIndex + 1)) / (100 ** _setBonusIndex)) * PRECISION;
        }
        //add pet bonuses
        if (_setBonusIndex == _getPetBonus1(_petData)) {
            bonus += int256(BONUS_DATA % (100 ** (_setBonusIndex + 1)) / (100 ** _setBonusIndex)) * PRECISION / 2;
        }
        if (_setBonusIndex == _getPetBonus2(_petData)) {
            bonus += int256(BONUS_DATA % (100 ** (_setBonusIndex + 1)) / (100 ** _setBonusIndex)) * PRECISION / 2;
        }
        //add warrior aura bonuses
        if (isAuraSet(_warriorAuras, uint8(_setBonusIndex))) {//warriors receive half bonuses from auras
            bonus += int256(BONUS_DATA % (100 ** (_setBonusIndex + 1)) / (100 ** _setBonusIndex)) * PRECISION / 2;
        }
        //add pet aura bonuses
        if (isAuraSet(_petAuras, uint8(_setBonusIndex))) {//pets receive full bonues from auras
            bonus += int256(BONUS_DATA % (100 ** (_setBonusIndex + 1)) / (100 ** _setBonusIndex)) * PRECISION;
        }
        return bonus;
    }
    
    function _computeRarityBonus(uint256 _rarity, uint256 hash) internal pure returns (uint256){
        if (_rarity == UNCOMMON) {
            return 1 + _random(0, BONUS_PENETRATION, hash, SPECIALITY_MASK_21, RARITY_BONUS_MASK_20);
        }
        if (_rarity == RARE) {
            return 1 + _random(BONUS_PENETRATION, BONUS_DAMAGE, hash, SPECIALITY_MASK_21, RARITY_BONUS_MASK_20);
        }
        if (_rarity >= MYTHIC) {
            return 1 + _random(0, BONUS_DAMAGE, hash, SPECIALITY_MASK_21, RARITY_BONUS_MASK_20);
        }
        return BONUS_NONE;
    }

    function _computeAura(uint256 _rarity, uint256 hash) internal pure returns (uint256){
        if (_rarity >= MYTHIC) {
            return 1 + _random(0, BONUS_DAMAGE, hash, BASE_MASK_24, AURA_MASK_23);
        }
        return BONUS_NONE;
    }
    
	function _computeRarity(uint256 _reward, uint256 _unique, uint256 _legendary, 
	    uint256 _mythic, uint256 _rare, uint256 _uncommon) internal pure returns(uint256){
	        
        uint256 range = _unique + _legendary + _mythic + _rare + _uncommon;
        if (_reward >= range) return COMMON; // common
        if (_reward >= (range = (range - _uncommon))) return UNCOMMON;
        if (_reward >= (range = (range - _rare))) return RARE;
        if (_reward >= (range = (range - _mythic))) return MYTHIC;
        if (_reward >= (range = (range - _legendary))) return LEGENDARY;
        if (_reward < range) return UNIQUE;
        return COMMON;
    }
    
    function _computeUniqueness(uint256 _rarity, uint256 nextUnique) internal pure returns (uint256){
        return _rarity == UNIQUE ? nextUnique : 0;
    }
    
    /* identity packing */
    /* @returns bonus value which depends on speciality value,
     * if speciality == 1 (miner), then bonus value will be equal 4,
     * otherwise 1
     */
    function _getBonus(uint256 identity) internal pure returns(uint256){
        return getSpecialityValue(identity) == MINER_PERK ? 4 : 1;
    }
    

    function _computeAndSetBaseParameters16_18_22(uint256 _hash) internal pure returns (uint256, uint256){
        uint256 identity = 0;

        uint256 damage = 35 + _random(0, 21, _hash, AURA_MASK_23, DAMAGE_MASK_22);
        
        uint256 strength = 45 + _random(0, 26, _hash, CLASS_MECH_MASK_19, STRENGTH_MASK_18);
        uint256 agility = 15 + (125 - damage - strength);
        uint256 intelligence = 155 - strength - agility - damage;
        (strength, agility, intelligence) = _shuffleParams(strength, agility, intelligence, _hash);
        
        identity += _setStrengthValue18(strength);
        identity += _setAgilityValue17(agility);
		identity += _setIntelligenceValue16(intelligence);
		identity += _setDamgeValue22(damage);
        
        uint256 classMech = strength > agility ? (strength > intelligence ? WARRIOR : MAGE) : (agility > intelligence ? ARCHER : MAGE);
        return (identity, classMech);
    }
    
    function _shuffleParams(uint256 param1, uint256 param2, uint256 param3, uint256 _hash) internal pure returns(uint256, uint256, uint256) {
        uint256 temp = param1;
        if (_hash % 2 == 0) {
            temp = param1;
            param1 = param2;
            param2 = temp;
        }
        if ((_hash / 10 % 2) == 0) {
            temp = param2;
            param2 = param3;
            param3 = temp;
        }
        if ((_hash / 100 % 2) == 0) {
            temp = param1;
            param1 = param2;
            param2 = temp;
        }
        return (param1, param2, param3);
    }
    
    
    /* RANDOM */
    function _random(uint256 _min, uint256 _max, uint256 _hash, uint256 _reminder, uint256 _devider) internal pure returns (uint256){
        return ((_hash % _reminder) / _devider) % (_max - _min) + _min;
    }

    function _random(uint256 _min, uint256 _max, uint256 _hash) internal pure returns (uint256){
        return _hash % (_max - _min) + _min;
    }

    function _getTargetBlock(uint256 _targetBlock) internal view returns(uint256){
        uint256 currentBlock = block.number;
        uint256 target = currentBlock - (currentBlock % 256) + (_targetBlock % 256);
        if (target >= currentBlock) {
            return (target - 256);
        }
        return target;
    }
    
    function _getMaxRarityChance() internal pure returns(uint256){
        return RARITY_CHANCE_RANGE;
    }
    
    function generateWarrior(uint256 _heroIdentity, uint256 _heroLevel, uint256 _targetBlock, uint256 specialPerc, uint32[19] memory params) internal view returns (uint256) {
        _targetBlock = _getTargetBlock(_targetBlock);
        
        uint256 identity;
        uint256 hash = uint256(keccak256(block.blockhash(_targetBlock), _heroIdentity, block.coinbase, block.difficulty));
        //0 _heroLevel produces warriors of COMMON rarity
        uint256 rarityChance = _heroLevel == 0 ? RARITY_CHANCE_RANGE : 
        	_random(0, RARITY_CHANCE_RANGE, hash) / (_heroLevel * _getBonus(_heroIdentity)); // 0 - 10 000 000
        uint256 rarity = _computeRarity(rarityChance, 
            params[UNIQUE_INDEX_13],params[LEGENDARY_INDEX_14], params[MYTHIC_INDEX_15], params[RARE_INDEX_16], params[UNCOMMON_INDEX_17]);
            
        uint256 classMech;
        
        // start
        (identity, classMech) = _computeAndSetBaseParameters16_18_22(hash);
        
        identity += _setUniqueValue0(_computeUniqueness(rarity, params[UNIQUE_TOTAL_INDEX_18] + 1));
        identity += _setRarityValue1(rarity);
        identity += _setClassViewValue2(classMech); // 1 to 1 with classMech
        
        identity += _setBodyColorValue3(1 + _random(0, params[BODY_COLOR_MAX_INDEX_0], hash, EYES_MASK_4, BODY_COLOR_MASK_3));
        identity += _setEyesValue4(1 + _random(0, params[EYES_MAX_INDEX_1], hash, MOUTH_MASK_5, EYES_MASK_4));
        identity += _setMouthValue5(1 + _random(0, params[MOUTH_MAX_2], hash, HEIR_MASK_6, MOUTH_MASK_5));
        identity += _setHairValue6(1 + _random(0, params[HAIR_MAX_3], hash, HEIR_COLOR_MASK_7, HEIR_MASK_6));
        identity += _setHairColorValue7(1 + _random(0, params[HEIR_COLOR_MAX_4], hash, ARMOR_MASK_8, HEIR_COLOR_MASK_7));
        identity += _setArmorValue8(1 + _random(0, params[ARMOR_MAX_5], hash, WEAPON_MASK_9, ARMOR_MASK_8));
        identity += _setWeaponValue9(1 + _random(0, params[WEAPON_MAX_6], hash, HAT_MASK_10, WEAPON_MASK_9));
        identity += _setHatValue10(_random(0, params[HAT_MAX_7], hash, RUNES_MASK_11, HAT_MASK_10));//removed +1
        
        identity += _setRunesValue11(_computeRunes(rarity));
        identity += _setWingsValue12(_computeWings(rarity, params[WINGS_MAX_9], hash));
        identity += _setPetValue13(_computePet(rarity, params[PET_MAX_10], hash));
        identity += _setBorderValue14(_computeBorder(rarity)); // 1 to 1 with rarity
        identity += _setBackgroundValue15(_computeBackground(rarity)); // 1 to 1 with rarity
        
        identity += _setClassMechValue19(classMech);

        identity += _setRarityBonusValue20(_computeRarityBonus(rarity, hash));
        identity += _setSpecialityValue21(specialPerc); // currently only miner (1)
        
        identity += _setAuraValue23(_computeAura(rarity, hash));
        // end
        return identity;
    }
    
	function _changeParameter(uint256 _paramIndex, uint32 _value, uint32[19] storage parameters) internal {
		//we can change only view parameters, and unique count in max range <= 100
		require(_paramIndex >= BODY_COLOR_MAX_INDEX_0 && _paramIndex <= UNIQUE_INDEX_13);
		//we can NOT set pet, border and background values,
		//those values have special logic behind them
		require(
		    _paramIndex != RUNES_MAX_8 && 
		    _paramIndex != PET_MAX_10 && 
		    _paramIndex != BORDER_MAX_11 && 
		    _paramIndex != BACKGROUND_MAX_12
		);
		//value of bodyColor, eyes, mouth, hair, hairColor, armor, weapon, hat must be < 1000
		require(_paramIndex > HAT_MAX_7 || _value < 1000);
		//value of wings,  must be < 100
		require(_paramIndex > BACKGROUND_MAX_12 || _value < 100);
		//check that max total number of UNIQUE warriors that we can emit is not > 100
		require(_paramIndex != UNIQUE_INDEX_13 || (_value + parameters[UNIQUE_TOTAL_INDEX_18]) <= 100);
		
		parameters[_paramIndex] = _value;
    }
    
	function _recordWarriorData(uint256 identity, uint32[19] storage parameters) internal {
        uint256 rarity = getRarityValue(identity);
        if (rarity == UNCOMMON) { // uncommon
            parameters[UNCOMMON_INDEX_17]--;
            return;
        }
        if (rarity == RARE) { // rare
            parameters[RARE_INDEX_16]--;
            return;
        }
        if (rarity == MYTHIC) { // mythic
            parameters[MYTHIC_INDEX_15]--;
            return;
        }
        if (rarity == LEGENDARY) { // legendary
            parameters[LEGENDARY_INDEX_14]--;
            return;
        }
        if (rarity == UNIQUE) { // unique
            parameters[UNIQUE_INDEX_13]--;
            parameters[UNIQUE_TOTAL_INDEX_18] ++;
            return;
        }
    }
    
    function _validateIdentity(uint256 _identity, uint32[19] memory params) internal pure returns(bool){
        uint256 rarity = getRarityValue(_identity);
        require(rarity <= UNIQUE);
        
        require(
            rarity <= COMMON ||//common 
            (rarity == UNCOMMON && params[UNCOMMON_INDEX_17] > 0) ||//uncommon
            (rarity == RARE && params[RARE_INDEX_16] > 0) ||//rare
            (rarity == MYTHIC && params[MYTHIC_INDEX_15] > 0) ||//mythic
            (rarity == LEGENDARY && params[LEGENDARY_INDEX_14] > 0) ||//legendary
            (rarity == UNIQUE && params[UNIQUE_INDEX_13] > 0)//unique
        );
        require(rarity != UNIQUE || getUniqueValue(_identity) > params[UNIQUE_TOTAL_INDEX_18]);
        
        //check battle parameters
        require(
            getStrengthValue(_identity) < 100 &&
            getAgilityValue(_identity) < 100 &&
            getIntelligenceValue(_identity) < 100 &&
            getDamageValue(_identity) <= 55
        );
        require(getClassMechValue(_identity) <= MAGE);
        require(getClassMechValue(_identity) == getClassViewValue(_identity));
        require(getSpecialityValue(_identity) <= MINER_PERK);
        require(getRarityBonusValue(_identity) <= BONUS_DAMAGE);
        require(getAuraValue(_identity) <= BONUS_DAMAGE);
        
        //check view
        require(getBodyColorValue(_identity) <= params[BODY_COLOR_MAX_INDEX_0]);
        require(getEyesValue(_identity) <= params[EYES_MAX_INDEX_1]);
        require(getMouthValue(_identity) <= params[MOUTH_MAX_2]);
        require(getHairValue(_identity) <= params[HAIR_MAX_3]);
        require(getHairColorValue(_identity) <= params[HEIR_COLOR_MAX_4]);
        require(getArmorValue(_identity) <= params[ARMOR_MAX_5]);
        require(getWeaponValue(_identity) <= params[WEAPON_MAX_6]);
        require(getHatValue(_identity) <= params[HAT_MAX_7]);
        require(getRunesValue(_identity) <= params[RUNES_MAX_8]);
        require(getWingsValue(_identity) <= params[WINGS_MAX_9]);
        require(getPetValue(_identity) <= params[PET_MAX_10]);
        require(getBorderValue(_identity) <= params[BORDER_MAX_11]);
        require(getBackgroundValue(_identity) <= params[BACKGROUND_MAX_12]);
        
        return true;
    }
    
    /* UNPACK METHODS */
    //common
    function _unpackClassValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % RARITY_PACK_2 / CLASS_PACK_0);
    }
    
    function _unpackRarityBonusValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % RARITY_PACK_2 / RARITY_BONUS_PACK_1);
    }
    
    function _unpackRarityValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % EXPERIENCE_PACK_3 / RARITY_PACK_2);
    }
    
    function _unpackExpValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % INTELLIGENCE_PACK_4 / EXPERIENCE_PACK_3);
    }

    function _unpackLevelValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % INTELLIGENCE_PACK_4) / (EXPERIENCE_PACK_3 * POINTS_TO_LEVEL);
    }
    
    function _unpackIntelligenceValue(uint256 packedValue) internal pure returns(int256){
        return int256(packedValue % AGILITY_PACK_5 / INTELLIGENCE_PACK_4);
    }
    
    function _unpackAgilityValue(uint256 packedValue) internal pure returns(int256){
        return int256(packedValue % STRENGTH_PACK_6 / AGILITY_PACK_5);
    }
    
    function _unpackStrengthValue(uint256 packedValue) internal pure returns(int256){
        return int256(packedValue % BASE_DAMAGE_PACK_7 / STRENGTH_PACK_6);
    }

    function _unpackBaseDamageValue(uint256 packedValue) internal pure returns(int256){
        return int256(packedValue % PET_PACK_8 / BASE_DAMAGE_PACK_7);
    }
    
    function _unpackPetValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % AURA_PACK_9 / PET_PACK_8);
    }
    
    function _unpackAuraValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % WARRIOR_ID_PACK_10 / AURA_PACK_9);
    }
    //
    //pvp unpack
    function _unpackIdValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % PVP_CYCLE_PACK_11 / WARRIOR_ID_PACK_10);
    }
    
    function _unpackCycleValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % RATING_PACK_12 / PVP_CYCLE_PACK_11);
    }
    
    function _unpackRatingValue(uint256 packedValue) internal pure returns(uint256){
        return (packedValue % PVP_BASE_PACK_13 / RATING_PACK_12);
    }
    
    //max cycle skip value cant be more than 1000000000
    function _changeCycleValue(uint256 packedValue, uint256 newValue) internal pure returns(uint256){
        newValue = newValue > 1000000000 ? 1000000000 : newValue;
        return packedValue - (_unpackCycleValue(packedValue) * PVP_CYCLE_PACK_11) + newValue * PVP_CYCLE_PACK_11;
    }
    
    function _packWarriorCommonData(uint256 _identity, uint256 _experience) internal pure returns(uint256){
        uint256 packedData = 0;
        packedData += getClassMechValue(_identity) * CLASS_PACK_0;
        packedData += getRarityBonusValue(_identity) * RARITY_BONUS_PACK_1;
        packedData += getRarityValue(_identity) * RARITY_PACK_2;
        packedData += _experience * EXPERIENCE_PACK_3;
        packedData += getIntelligenceValue(_identity) * INTELLIGENCE_PACK_4;
        packedData += getAgilityValue(_identity) * AGILITY_PACK_5;
        packedData += getStrengthValue(_identity) * STRENGTH_PACK_6;
        packedData += getDamageValue(_identity) * BASE_DAMAGE_PACK_7;
        packedData += getPetValue(_identity) * PET_PACK_8;
        
        return packedData;
    }
    
    function _packWarriorPvpData(uint256 _identity, uint256 _rating, uint256 _pvpCycle, uint256 _warriorId, uint256 _experience) internal pure returns(uint256){
        uint256 packedData = _packWarriorCommonData(_identity, _experience);
        packedData += _warriorId * WARRIOR_ID_PACK_10;
        packedData += _pvpCycle * PVP_CYCLE_PACK_11;
        //rating MUST have most significant value!
        packedData += _rating * RATING_PACK_12;
        return packedData;
    }
    
    /* TOURNAMENT BATTLES */
    
    
    function _packWarriorIds(uint256[] memory packedWarriors) internal pure returns(uint256){
        uint256 packedIds = 0;
        uint256 length = packedWarriors.length;
        for(uint256 i = 0; i < length; i ++) {
            packedIds += (MAX_ID_SIZE ** i) * _unpackIdValue(packedWarriors[i]);
        }
        return packedIds;
    }

    function _unpackWarriorId(uint256 packedIds, uint256 index) internal pure returns(uint256){
        return (packedIds % (MAX_ID_SIZE ** (index + 1)) / (MAX_ID_SIZE ** index));
    }
    
    function _packCombinedParams(int256 hp, int256 damage, int256 armor, int256 dodge, int256 penetration) internal pure returns(uint256) {
        uint256 combinedWarrior = 0;
        combinedWarrior += uint256(hp) * HP_PACK_0;
        combinedWarrior += uint256(damage) * DAMAGE_PACK_1;
        combinedWarrior += uint256(armor) * ARMOR_PACK_2;
        combinedWarrior += uint256(dodge) * DODGE_PACK_3;
        combinedWarrior += uint256(penetration) * PENETRATION_PACK_4;
        return combinedWarrior;
    }
    
    function _unpackProtectionParams(uint256 combinedWarrior) internal pure returns 
    (int256 hp, int256 armor, int256 dodge){
        hp = int256(combinedWarrior % DAMAGE_PACK_1 / HP_PACK_0);
        armor = int256(combinedWarrior % DODGE_PACK_3 / ARMOR_PACK_2);
        dodge = int256(combinedWarrior % PENETRATION_PACK_4 / DODGE_PACK_3);
    }
    
    function _unpackAttackParams(uint256 combinedWarrior) internal pure returns(int256 damage, int256 penetration) {
        damage = int256(combinedWarrior % ARMOR_PACK_2 / DAMAGE_PACK_1);
        penetration = int256(combinedWarrior % COMBINE_BASE_PACK_5 / PENETRATION_PACK_4);
    }
    
    function _combineWarriors(uint256[] memory packedWarriors) internal pure returns (uint256) {
        int256 hp;
        int256 damage;
		int256 armor;
		int256 dodge;
		int256 penetration;
		
		(hp, damage, armor, dodge, penetration) = _computeCombinedParams(packedWarriors);
        return _packCombinedParams(hp, damage, armor, dodge, penetration);
    }
    
    function _computeCombinedParams(uint256[] memory packedWarriors) internal pure returns 
    (int256 totalHp, int256 totalDamage, int256 maxArmor, int256 maxDodge, int256 maxPenetration){
        uint256 length = packedWarriors.length;
        
        int256 hp;
		int256 armor;
		int256 dodge;
		int256 penetration;
		
		uint256 warriorAuras;
		uint256 petAuras;
		(warriorAuras, petAuras) = _getAurasData(packedWarriors);
		
		uint256 packedWarrior;
        for(uint256 i = 0; i < length; i ++) {
            packedWarrior = packedWarriors[i];
            
            totalDamage += getDamage(packedWarrior, warriorAuras, petAuras);
            
            penetration = getPenetration(packedWarrior, warriorAuras, petAuras);
            maxPenetration = maxPenetration > penetration ? maxPenetration : penetration;
			(hp, armor, dodge) = _getProtectionParams(packedWarrior, warriorAuras, petAuras);
            totalHp += hp;
            maxArmor = maxArmor > armor ? maxArmor : armor;
            maxDodge = maxDodge > dodge ? maxDodge : dodge;
        }
    }
    
    function _getAurasData(uint256[] memory packedWarriors) internal pure returns(uint256 warriorAuras, uint256 petAuras) {
        uint256 length = packedWarriors.length;
        
        warriorAuras = 0;
        petAuras = 0;
        
        uint256 packedWarrior;
        for(uint256 i = 0; i < length; i ++) {
            packedWarrior = packedWarriors[i];
            warriorAuras = enableAura(warriorAuras, (_unpackAuraValue(packedWarrior)));
            petAuras = enableAura(petAuras, (_getPetAura(_unpackPetData(_unpackPetValue(packedWarrior)))));
        }
        warriorAuras = filterWarriorAuras(warriorAuras, petAuras);
        return (warriorAuras, petAuras);
    }
    
    // Get bit value at position
    function isAuraSet(uint256 aura, uint256 auraIndex) internal pure returns (bool) {
        return aura & (uint256(0x01) << auraIndex) != 0;
    }
    
    // Set bit value at position
    function enableAura(uint256 a, uint256 n) internal pure returns (uint256) {
        return a | (uint256(0x01) << n);
    }
    
    //switch off warrior auras that are enabled in pets auras, pet aura have priority
    function filterWarriorAuras(uint256 _warriorAuras, uint256 _petAuras) internal pure returns(uint256) {
        return (_warriorAuras & _petAuras) ^ _warriorAuras;
    }
  
    function _getTournamentBattles(uint256 _numberOfContenders) internal pure returns(uint256) {
        return (_numberOfContenders * BATTLES_PER_CONTENDER / 2);
    }
    
    function getTournamentBattleResults(uint256[] memory combinedWarriors, uint256 _targetBlock) internal view returns (uint32[] memory results){
        uint256 length = combinedWarriors.length;
        results = new uint32[](length);
		
		int256 damage1;
		int256 penetration1;
		
		uint256 hash;
		
		uint256 randomIndex;
		uint256 exp = 0;
		uint256 i;
		uint256 result;
        for(i = 0; i < length; i ++) {
            (damage1, penetration1) = _unpackAttackParams(combinedWarriors[i]);
            while(results[i] < BATTLES_PER_CONTENDER_SUM) {
                //if we just started generate new random source
                //or regenerate if we used all data from it
                if (exp == 0 || exp > 73) {
                    hash = uint256(keccak256(block.blockhash(_getTargetBlock(_targetBlock - i)), uint256(damage1) + now));
                    exp = 0;
                }
                //we do not fight with self if there are other warriors
                randomIndex = (_random(i + 1 < length ? i + 1 : i, length, hash, 1000 * 10**exp, 10**exp));
                result = getTournamentBattleResult(damage1, penetration1, combinedWarriors[i],
                    combinedWarriors[randomIndex], hash % (1000 * 10**exp) / 10**exp);
                results[result == 1 ? i : randomIndex] += 101;//icrement battle count 100 and +1 win
                results[result == 1 ? randomIndex : i] += 100;//increment only battle count 100 for loser
                if (results[randomIndex] >= BATTLES_PER_CONTENDER_SUM) {
                    if (randomIndex < length - 1) {
                        _swapValues(combinedWarriors, results, randomIndex, length - 1);
                    }
                    length --;
                }
                exp++;
            }
        }
        //filter battle count from results
        length = combinedWarriors.length;
        for(i = 0; i < length; i ++) {
            results[i] = results[i] % 100;
        }
        
        return results;
    }
    
    function _swapValues(uint256[] memory combinedWarriors, uint32[] memory results, uint256 id1, uint256 id2) internal pure {
        uint256 temp = combinedWarriors[id1];
        combinedWarriors[id1] = combinedWarriors[id2];
        combinedWarriors[id2] = temp;
        temp = results[id1];
        results[id1] = results[id2];
        results[id2] = uint32(temp);
    }

    function getTournamentBattleResult(int256 damage1, int256 penetration1, uint256 combinedWarrior1, 
        uint256 combinedWarrior2, uint256 randomSource) internal pure returns (uint256)
    {
        int256 damage2;
		int256 penetration2;
        
		(damage2, penetration2) = _unpackAttackParams(combinedWarrior1);

		int256 totalHp1 = getCombinedTotalHP(combinedWarrior1, penetration2);
		int256 totalHp2 = getCombinedTotalHP(combinedWarrior2, penetration1);
        
        return _getBattleResult(damage1 * getBattleRandom(randomSource, 1) / 100, damage2 * getBattleRandom(randomSource, 10) / 100, totalHp1, totalHp2, randomSource);
    }
    /* COMMON BATTLE */
    
    function _getBattleResult(int256 damage1, int256 damage2, int256 totalHp1, int256 totalHp2, uint256 randomSource)  internal pure returns (uint256){
		totalHp1 = (totalHp1 * (PRECISION * PRECISION) / damage2);
		totalHp2 = (totalHp2 * (PRECISION * PRECISION) / damage1);
		//if draw, let the coin decide who wins
		if (totalHp1 == totalHp2) return randomSource % 2 + 1;
		return totalHp1 > totalHp2 ? 1 : 2;       
    }
    
    function getCombinedTotalHP(uint256 combinedData, int256 enemyPenetration) internal pure returns(int256) {
        int256 hp;
		int256 armor;
		int256 dodge;
		(hp, armor, dodge) = _unpackProtectionParams(combinedData);
        
        return _getTotalHp(hp, armor, dodge, enemyPenetration);
    }
    
    function getTotalHP(uint256 packedData, uint256 warriorAuras, uint256 petAuras, int256 enemyPenetration) internal pure returns(int256) {
        int256 hp;
		int256 armor;
		int256 dodge;
		(hp, armor, dodge) = _getProtectionParams(packedData, warriorAuras, petAuras);
        
        return _getTotalHp(hp, armor, dodge, enemyPenetration);
    }
    
    function _getTotalHp(int256 hp, int256 armor, int256 dodge, int256 enemyPenetration) internal pure returns(int256) {
        int256 piercingResult = (armor - enemyPenetration) < -(75 * PRECISION) ? -(75 * PRECISION) : (armor - enemyPenetration);
        int256 mitigation = (PRECISION - piercingResult * PRECISION / (PRECISION + piercingResult / 100) / 100);
        
        return (hp * PRECISION / mitigation + (hp * dodge / (100 * PRECISION)));
    }
    
    function _applyLevelBonus(int256 _value, uint256 _level) internal pure returns(int256) {
        _level -= 1;
        return int256(uint256(_value) * (LEVEL_BONUSES % (100 ** (_level + 1)) / (100 ** _level)) / 10);
    }
    
    function _getProtectionParams(uint256 packedData, uint256 warriorAuras, uint256 petAuras) internal pure returns(int256 hp, int256 armor, int256 dodge) {
        uint256 rarityBonus = _unpackRarityBonusValue(packedData);
        uint256 petData = _unpackPetData(_unpackPetValue(packedData));
        int256 strength = _unpackStrengthValue(packedData) * PRECISION + _getBattleBonus(BONUS_STR, rarityBonus, petData, warriorAuras, petAuras);
        int256 agility = _unpackAgilityValue(packedData) * PRECISION + _getBattleBonus(BONUS_AGI, rarityBonus, petData, warriorAuras, petAuras);
        
        hp = 100 * PRECISION + strength + 7 * strength / 10 + _getBattleBonus(BONUS_HP, rarityBonus, petData, warriorAuras, petAuras);//add bonus hp
        hp = _applyLevelBonus(hp, _unpackLevelValue(packedData));
		armor = (strength + 8 * strength / 10 + agility + _getBattleBonus(BONUS_ARMOR, rarityBonus, petData, warriorAuras, petAuras));//add bonus armor
		dodge = (2 * agility / 3);
    }
    
    function getDamage(uint256 packedWarrior, uint256 warriorAuras, uint256 petAuras) internal pure returns(int256) {
        uint256 rarityBonus = _unpackRarityBonusValue(packedWarrior);
        uint256 petData = _unpackPetData(_unpackPetValue(packedWarrior));
        int256 agility = _unpackAgilityValue(packedWarrior) * PRECISION + _getBattleBonus(BONUS_AGI, rarityBonus, petData, warriorAuras, petAuras);
        int256 intelligence = _unpackIntelligenceValue(packedWarrior) * PRECISION + _getBattleBonus(BONUS_INT, rarityBonus, petData, warriorAuras, petAuras);
		
		int256 crit = (agility / 5 + intelligence / 4) + _getBattleBonus(BONUS_CRIT_CHANCE, rarityBonus, petData, warriorAuras, petAuras);
		int256 critMultiplier = (PRECISION + intelligence / 25) + _getBattleBonus(BONUS_CRIT_MULT, rarityBonus, petData, warriorAuras, petAuras);
        
        int256 damage = int256(_unpackBaseDamageValue(packedWarrior) * 3 * PRECISION / 2) + _getBattleBonus(BONUS_DAMAGE, rarityBonus, petData, warriorAuras, petAuras);
        
		return (_applyLevelBonus(damage, _unpackLevelValue(packedWarrior)) * (PRECISION + crit * critMultiplier / (100 * PRECISION))) / PRECISION;
    }

    function getPenetration(uint256 packedWarrior, uint256 warriorAuras, uint256 petAuras) internal pure returns(int256) {
        uint256 rarityBonus = _unpackRarityBonusValue(packedWarrior);
        uint256 petData = _unpackPetData(_unpackPetValue(packedWarrior));
        int256 agility = _unpackAgilityValue(packedWarrior) * PRECISION + _getBattleBonus(BONUS_AGI, rarityBonus, petData, warriorAuras, petAuras);
        int256 intelligence = _unpackIntelligenceValue(packedWarrior) * PRECISION + _getBattleBonus(BONUS_INT, rarityBonus, petData, warriorAuras, petAuras);
		
		return (intelligence * 2 + agility + _getBattleBonus(BONUS_PENETRATION, rarityBonus, petData, warriorAuras, petAuras));
    }
    
    /* BATTLE PVP */
    
    //@param randomSource must be >= 1000
    function getBattleRandom(uint256 randmSource, uint256 _step) internal pure returns(int256){
        return int256(100 + _random(0, 11, randmSource, 100 * _step, _step));
    }
    
    uint256 internal constant NO_AURA = 0;
    
    function getPVPBattleResult(uint256 packedData1, uint256 packedData2, uint256 randmSource) internal pure returns (uint256){
        uint256 petAura1 = _computePVPPetAura(packedData1);
        uint256 petAura2 = _computePVPPetAura(packedData2);
        
        uint256 warriorAura1 = _computePVPWarriorAura(packedData1, petAura1);
        uint256 warriorAura2 = _computePVPWarriorAura(packedData2, petAura2);
        
		int256 damage1 = getDamage(packedData1, warriorAura1, petAura1) * getBattleRandom(randmSource, 1) / 100;
        int256 damage2 = getDamage(packedData2, warriorAura2, petAura2) * getBattleRandom(randmSource, 10) / 100;

		int256 totalHp1;
		int256 totalHp2;
		(totalHp1, totalHp2) = _computeContendersTotalHp(packedData1, warriorAura1, petAura1, packedData2, warriorAura1, petAura1);
        
        return _getBattleResult(damage1, damage2, totalHp1, totalHp2, randmSource);
    }
    
    function _computePVPPetAura(uint256 packedData) internal pure returns(uint256) {
        return enableAura(NO_AURA, _getPetAura(_unpackPetData(_unpackPetValue(packedData))));
    }
    
    function _computePVPWarriorAura(uint256 packedData, uint256 petAuras) internal pure returns(uint256) {
        return filterWarriorAuras(enableAura(NO_AURA, _unpackAuraValue(packedData)), petAuras);
    }
    
    function _computeContendersTotalHp(uint256 packedData1, uint256 warriorAura1, uint256 petAura1, uint256 packedData2, uint256 warriorAura2, uint256 petAura2) 
    internal pure returns(int256 totalHp1, int256 totalHp2) {
		int256 enemyPenetration = getPenetration(packedData2, warriorAura2, petAura2);
		totalHp1 = getTotalHP(packedData1, warriorAura1, petAura1, enemyPenetration);
		enemyPenetration = getPenetration(packedData1, warriorAura1, petAura1);
		totalHp2 = getTotalHP(packedData2, warriorAura1, petAura1, enemyPenetration);
    }
    
    function getRatingRange(uint256 _pvpCycle, uint256 _pvpInterval, uint256 _expandInterval) internal pure returns (uint256){
        return 50 + (_pvpCycle * _pvpInterval / _expandInterval * 25);
    }
    
    function isMatching(int256 evenRating, int256 oddRating, int256 ratingGap) internal pure returns(bool) {
        return evenRating <= (oddRating + ratingGap) && evenRating >= (oddRating - ratingGap);
    }
    
    function sort(uint256[] memory data) internal pure {
       quickSort(data, int(0), int(data.length - 1));
    }
    
    function quickSort(uint256[] memory arr, int256 left, int256 right) internal pure {
        int256 i = left;
        int256 j = right;
        if(i==j) return;
        uint256 pivot = arr[uint256(left + (right - left) / 2)];
        while (i <= j) {
            while (arr[uint256(i)] < pivot) i++;
            while (pivot < arr[uint256(j)]) j--;
            if (i <= j) {
                (arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]);
                i++;
                j--;
            }
        }
        if (left < j)
            quickSort(arr, left, j);
        if (i < right)
            quickSort(arr, i, right);
    }
    
    function _swapPair(uint256[] memory matchingIds, uint256 id1, uint256 id2, uint256 id3, uint256 id4) internal pure {
        uint256 temp = matchingIds[id1];
        matchingIds[id1] = matchingIds[id2];
        matchingIds[id2] = temp;
        
        temp = matchingIds[id3];
        matchingIds[id3] = matchingIds[id4];
        matchingIds[id4] = temp;
    }
    
    function _swapValues(uint256[] memory matchingIds, uint256 id1, uint256 id2) internal pure {
        uint256 temp = matchingIds[id1];
        matchingIds[id1] = matchingIds[id2];
        matchingIds[id2] = temp;
    }
    
    function _getMatchingIds(uint256[] memory matchingIds, uint256 _pvpInterval, uint256 _skipCycles, uint256 _expandInterval) 
    internal pure returns(uint256 matchingCount) 
    {
        matchingCount = matchingIds.length;
        if (matchingCount == 0) return 0;
        
        uint256 warriorId;
        uint256 index;
        //sort matching ids
        quickSort(matchingIds, int256(0), int256(matchingCount - 1));
        //find pairs
        int256 rating1;
        uint256 pairIndex = 0;
        int256 ratingRange;
        for(index = 0; index < matchingCount; index++) {
            //get packed value
            warriorId = matchingIds[index];
            //unpack rating 1
            rating1 = int256(_unpackRatingValue(warriorId));
            ratingRange = int256(getRatingRange(_unpackCycleValue(warriorId) + _skipCycles, _pvpInterval, _expandInterval));
            
            if (index > pairIndex && //check left neighbor
            isMatching(rating1, int256(_unpackRatingValue(matchingIds[index - 1])), ratingRange)) {
                //move matched pairs to the left
                //swap pairs
                _swapPair(matchingIds, pairIndex, index - 1, pairIndex + 1, index);
                //mark last pair position
                pairIndex += 2;
            } else if (index + 1 < matchingCount && //check right neighbor
            isMatching(rating1, int256(_unpackRatingValue(matchingIds[index + 1])), ratingRange)) {
                //move matched pairs to the left
                //swap pairs
                _swapPair(matchingIds, pairIndex, index, pairIndex + 1, index + 1);
                //mark last pair position
                pairIndex += 2;
                //skip next iteration
                index++;
            }
        }
        
        matchingCount = pairIndex;
    }

    function _getPVPBattleResults(uint256[] memory matchingIds, uint256 matchingCount, uint256 _targetBlock) internal view {
        uint256 exp = 0;
        uint256 hash = 0;
        uint256 result = 0;
        for (uint256 even = 0; even < matchingCount; even += 2) {
            if (exp == 0 || exp > 73) {
                hash = uint256(keccak256(block.blockhash(_getTargetBlock(_targetBlock)), hash));
                exp = 0;
            }
                
            //compute battle result 1 = even(left) id won, 2 - odd(right) id won
            result = getPVPBattleResult(matchingIds[even], matchingIds[even + 1], hash % (1000 * 10**exp) / 10**exp);
            require(result > 0 && result < 3);
            exp++;
            //if odd warrior won, swap his id with even warrior,
            //otherwise do nothing,
            //even ids are winning ids! odds suck!
            if (result == 2) {
                _swapValues(matchingIds, even, even + 1);
            }
        }
    }
    
    function _getLevel(uint256 _levelPoints) internal pure returns(uint256) {
        return _levelPoints / POINTS_TO_LEVEL;
    }
    
}

library DataTypes {
     // / @dev The main Warrior struct. Every warrior in CryptoWarriors is represented by a copy
    // /  of this structure, so great care was taken to ensure that it fits neatly into
    // /  exactly two 256-bit words. Note that the order of the members in this structure
    // /  is important because of the byte-packing rules used by Ethereum.
    // /  Ref: http://solidity.readthedocs.io/en/develop/miscellaneous.html
    struct Warrior{
        // The Warrior's identity code is packed into these 256-bits
        uint256 identity;
        
        uint64 cooldownEndBlock;
        /** every warriors starts from 1 lv (10 level points per level) */
        uint64 level;
        /** PVP rating, every warrior starts with 100 rating */
        int64 rating;
        // 0 - idle
        uint32 action;
        /** Set to the index in the levelRequirements array (see CryptoWarriorBase.levelRequirements) that represents
         *  the current dungeon level requirement for warrior. This starts at zero. */
        uint32 dungeonIndex;
    }
}

contract CryptoWarriorBase is PermissionControll, PVPListenerInterface {

    /*** EVENTS ***/

    /// @dev The Arise event is fired whenever a new warrior comes into existence. This obviously
    ///  includes any time a warrior is created through the ariseWarrior method, but it is also called
    ///  when a new miner warrior is created.
    event Arise(address owner, uint256 warriorId, uint256 identity);

    /// @dev Transfer event as defined in current draft of ERC721. Emitted every time a warrior
    ///  ownership is assigned, including dungeon rewards.
    event Transfer(address from, address to, uint256 tokenId);

    /*** CONSTANTS ***/
    
	uint256 public constant IDLE = 0;
    uint256 public constant PVE_BATTLE = 1;
    uint256 public constant PVP_BATTLE = 2;
    uint256 public constant TOURNAMENT_BATTLE = 3;
    
    //max pve dungeon level
    uint256 public constant MAX_LEVEL = 25;
    //how many points is needed to get 1 level
    uint256 public constant POINTS_TO_LEVEL = 10;
    
    /// @dev A lookup table contains PVE dungeon level requirements, each time warrior
    /// completes dungeon, next level requirement is set, until 25lv (250points) is reached.
    uint32[6] public dungeonRequirements = [
        uint32(10),
        uint32(30),
        uint32(60),
        uint32(100),
        uint32(150),
        uint32(250)
    ];

    // An approximation of currently how many seconds are in between blocks.
    uint256 public secondsPerBlock = 15;

    /*** STORAGE ***/

    /// @dev An array containing the Warrior struct for all Warriors in existence. The ID
    ///  of each warrior is actually an index of this array.
    DataTypes.Warrior[] warriors;

    /// @dev A mapping from warrior IDs to the address that owns them. All warriors have
    ///  some valid owner address, even miner warriors are created with a non-zero owner.
    mapping (uint256 => address) public warriorToOwner;

    // @dev A mapping from owner address to count of tokens that address owns.
    //  Used internally inside balanceOf() to resolve ownership count.
    mapping (address => uint256) ownersTokenCount;

    /// @dev A mapping from warrior IDs to an address that has been approved to call
    ///  transferFrom(). Each Warrior can only have one approved address for transfer
    ///  at any time. A zero value means no approval is outstanding.
    mapping (uint256 => address) public warriorToApproved;
    
    // Mapping from owner to list of owned token IDs
    mapping (address => uint256[]) internal ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) internal ownedTokensIndex;


    /// @dev The address of the ClockAuction contract that handles sales of warriors. This
    ///  same contract handles both peer-to-peer sales as well as the miner sales which are
    ///  initiated every 15 minutes.
    SaleClockAuction public saleAuction;
    
    
    /// @dev Assigns ownership of a specific warrior to an address.
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // When creating new warriors _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
            _clearApproval(_tokenId);
            _removeTokenFrom(_from, _tokenId);
        }
        _addTokenTo(_to, _tokenId);
        
        // Emit the transfer event.
        Transfer(_from, _to, _tokenId);
    }
    
    function _addTokenTo(address _to, uint256 _tokenId) internal {
        // Since the number of warriors is capped to '1 000 000' we can't overflow this
        ownersTokenCount[_to]++;
        // transfer ownership
        warriorToOwner[_tokenId] = _to;
        
        uint256 length = ownedTokens[_to].length;
        ownedTokens[_to].push(_tokenId);
        ownedTokensIndex[_tokenId] = length;
    }
    
    function _removeTokenFrom(address _from, uint256 _tokenId) internal {
        //
        ownersTokenCount[_from]--;
        
        warriorToOwner[_tokenId] = address(0);
        
        uint256 tokenIndex = ownedTokensIndex[_tokenId];
        uint256 lastTokenIndex = ownedTokens[_from].length - 1;
        uint256 lastToken = ownedTokens[_from][lastTokenIndex];
    
        ownedTokens[_from][tokenIndex] = lastToken;
        ownedTokens[_from][lastTokenIndex] = 0;
        
        // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
        // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
        // the lastToken to the first position, and then dropping the element placed in the last position of the list
        ownedTokens[_from].length--;
        ownedTokensIndex[_tokenId] = 0;
        ownedTokensIndex[lastToken] = tokenIndex;
    }
    
    function _clearApproval(uint256 _tokenId) internal {
        if (warriorToApproved[_tokenId] != address(0)) {
            // clear any previously approved ownership exchange
            warriorToApproved[_tokenId] = address(0);
        }
    }
    
    function _createWarrior(uint256 _identity, address _owner, uint256 _cooldown, uint256 _level, uint256 _rating, uint256 _dungeonIndex)
        internal
        returns (uint256) {
        	    
        DataTypes.Warrior memory _warrior = DataTypes.Warrior({
            identity : _identity,
            cooldownEndBlock : uint64(_cooldown),
            level : uint64(_level),//uint64(10),
            rating : int64(_rating),//int64(100),
            action : uint32(IDLE),
            dungeonIndex : uint32(_dungeonIndex)//uint32(0)
        });
        uint256 newWarriorId = warriors.push(_warrior) - 1;
        
        // let's just be 100% sure we never let this happen.
        require(newWarriorId == uint256(uint32(newWarriorId)));
        
        // emit the arise event
        Arise(_owner, newWarriorId, _identity);
        
        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        _transfer(0, _owner, newWarriorId);

        return newWarriorId;
    }
    

    // Any C-level can fix how many seconds per blocks are currently observed.
    function setSecondsPerBlock(uint256 secs) external onlyAuthorized {
        secondsPerBlock = secs;
    }
}

contract WarriorTokenImpl is CryptoWarriorBase, ERC721 {

    /// @notice Name and symbol of the non fungible token, as defined in ERC721.
    string public constant name = "CryptoWarriors";
    string public constant symbol = "CW";

    bytes4 constant InterfaceSignature_ERC165 =
        bytes4(keccak256('supportsInterface(bytes4)'));

    bytes4 constant InterfaceSignature_ERC721 =
        bytes4(keccak256('name()')) ^
        bytes4(keccak256('symbol()')) ^
        bytes4(keccak256('totalSupply()')) ^
        bytes4(keccak256('balanceOf(address)')) ^
        bytes4(keccak256('ownerOf(uint256)')) ^
        bytes4(keccak256('approve(address,uint256)')) ^
        bytes4(keccak256('transfer(address,uint256)')) ^
        bytes4(keccak256('transferFrom(address,address,uint256)')) ^
        bytes4(keccak256('tokensOfOwner(address)'));

    /// @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165).
    ///  Returns true for any standardized interfaces implemented by this contract. We implement
    ///  ERC-165 (obviously!) and ERC-721.
    function supportsInterface(bytes4 _interfaceID) external view returns (bool)
    {
        // DEBUG ONLY
        //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9f40b779));

        return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
    }

    // Internal utility functions: These functions all assume that their input arguments
    // are valid. We leave it to public methods to sanitize their inputs and follow
    // the required logic.

    /** @dev Checks if a given address is the current owner of the specified Warrior tokenId.
     * @param _claimant the address we are validating against.
     * @param _tokenId warrior id
     */
    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return _claimant != address(0) && warriorToOwner[_tokenId] == _claimant;    
    }

    function _ownerApproved(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return _claimant != address(0) &&//0 address means token is burned 
        warriorToOwner[_tokenId] == _claimant && warriorToApproved[_tokenId] == address(0);    
    }

    /// @dev Checks if a given address currently has transferApproval for a particular Warrior.
    /// @param _claimant the address we are confirming warrior is approved for.
    /// @param _tokenId warrior id
    function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return warriorToApproved[_tokenId] == _claimant;
    }

    /// @dev Marks an address as being approved for transferFrom(), overwriting any previous
    ///  approval. Setting _approved to address(0) clears all transfer approval.
    ///  NOTE: _approve() does NOT send the Approval event. This is intentional because
    ///  _approve() and transferFrom() are used together for putting Warriors on auction, and
    ///  there is no value in spamming the log with Approval events in that case.
    function _approve(uint256 _tokenId, address _approved) internal {
        warriorToApproved[_tokenId] = _approved;
    }

    /// @notice Returns the number of Warriors(tokens) owned by a specific address.
    /// @param _owner The owner address to check.
    /// @dev Required for ERC-721 compliance
    function balanceOf(address _owner) public view returns (uint256 count) {
        return ownersTokenCount[_owner];
    }

    /// @notice Transfers a Warrior to another address. If transferring to a smart
    ///  contract be VERY CAREFUL to ensure that it is aware of ERC-721 (or
    ///  CryptoWarriors specifically) or your Warrior may be lost forever. Seriously.
    /// @param _to The address of the recipient, can be a user or contract.
    /// @param _tokenId The ID of the Warrior to transfer.
    /// @dev Required for ERC-721 compliance.
    function transfer(address _to, uint256 _tokenId) external whenNotPaused {
        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));
        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any warriors (except very briefly
        // after a miner warrior is created and before it goes on auction).
        require(_to != address(this));
        // Disallow transfers to the auction contracts to prevent accidental
        // misuse. Auction contracts should only take ownership of warriors
        // through the allow + transferFrom flow.
        require(_to != address(saleAuction));
        // You can only send your own warrior.
        require(_owns(msg.sender, _tokenId));
        // Only idle warriors are allowed 
        require(warriors[_tokenId].action == IDLE);

        // Reassign ownership, clear pending approvals, emit Transfer event.
        _transfer(msg.sender, _to, _tokenId);
    }
    
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256) {
        require(_index < balanceOf(_owner));
        return ownedTokens[_owner][_index];
    }

    /// @notice Grant another address the right to transfer a specific Warrior via
    ///  transferFrom(). This is the preferred flow for transfering NFTs to contracts.
    /// @param _to The address to be granted transfer approval. Pass address(0) to
    ///  clear all approvals.
    /// @param _tokenId The ID of the Warrior that can be transferred if this call succeeds.
    /// @dev Required for ERC-721 compliance.
    function approve(address _to, uint256 _tokenId) external whenNotPaused {
        // Only an owner can grant transfer approval.
        require(_owns(msg.sender, _tokenId));
        // Only idle warriors are allowed 
        require(warriors[_tokenId].action == IDLE);

        // Register the approval (replacing any previous approval).
        _approve(_tokenId, _to);

        // Emit approval event.
        Approval(msg.sender, _to, _tokenId);
    }

    /// @notice Transfer a Warrior owned by another address, for which the calling address
    ///  has previously been granted transfer approval by the owner.
    /// @param _from The address that owns the Warrior to be transfered.
    /// @param _to The address that should take ownership of the Warrior. Can be any address,
    ///  including the caller.
    /// @param _tokenId The ID of the Warrior to be transferred.
    /// @dev Required for ERC-721 compliance.
    function transferFrom(address _from, address _to, uint256 _tokenId)
        external
        whenNotPaused
    {
        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));
        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any warriors (except very briefly
        // after a miner warrior is created and before it goes on auction).
        require(_to != address(this));
        // Check for approval and valid ownership
        require(_approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId));
        // Only idle warriors are allowed 
        require(warriors[_tokenId].action == IDLE);

        // Reassign ownership (also clears pending approvals and emits Transfer event).
        _transfer(_from, _to, _tokenId);
    }

    /// @notice Returns the total number of Warriors currently in existence.
    /// @dev Required for ERC-721 compliance.
    function totalSupply() public view returns (uint256) {
        return warriors.length;
    }

    /// @notice Returns the address currently assigned ownership of a given Warrior.
    /// @dev Required for ERC-721 compliance.
    function ownerOf(uint256 _tokenId)
        external
        view
        returns (address owner)
    {
        require(_tokenId < warriors.length);
        owner = warriorToOwner[_tokenId];
    }

    /// @notice Returns a list of all Warrior IDs assigned to an address.
    /// @param _owner The owner whose Warriors we are interested in.
    function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
        return ownedTokens[_owner];
    }
    
    function tokensOfOwnerFromIndex(address _owner, uint256 _fromIndex, uint256 _count) external view returns(uint256[] memory ownerTokens) {
        require(_fromIndex < balanceOf(_owner));
        uint256[] storage tokens = ownedTokens[_owner];
        //        
        uint256 ownerBalance = ownersTokenCount[_owner];
        uint256 lenght = (ownerBalance - _fromIndex >= _count ? _count : ownerBalance - _fromIndex);
        //
        ownerTokens = new uint256[](lenght);
        for(uint256 i = 0; i < lenght; i ++) {
            ownerTokens[i] = tokens[_fromIndex + i];
        }
        
        return ownerTokens;
    }
    
    /**
     * @dev Internal function to burn a specific token
     * @dev Reverts if the token does not exist
     * @param _owner owner of the token to burn
     * @param _tokenId uint256 ID of the token being burned by the msg.sender
     */
    function _burn(address _owner, uint256 _tokenId) internal {
        _clearApproval(_tokenId);
        _removeTokenFrom(_owner, _tokenId);
        
        Transfer(_owner, address(0), _tokenId);
    }

}

contract CryptoWarriorPVE is WarriorTokenImpl {
    uint256 internal constant MINER_PERK = 1;
    uint256 internal constant SUMMONING_SICKENESS = 12;
    
    uint256 internal constant PVE_COOLDOWN = 1 hours;
    uint256 internal constant PVE_DURATION = 15 minutes;
    
    
    /// @notice The payment required to use startPVEBattle().
    uint256 public pveBattleFee = 10 finney;
    uint256 public constant PVE_COMPENSATION = 2 finney;
    
	/// @dev The address of the sibling contract that is used to implement warrior generation algorithm.
    SanctuaryInterface public sanctuary;

    /** @dev PVEStarted event. Emitted every time a warrior enters pve battle
     *  @param owner Warrior owner
     *  @param dungeonIndex Started dungeon index 
     *  @param warriorId Warrior ID that started PVE dungeon
     *  @param battleEndBlock Block number, when started PVE dungeon will be completed
     */
    event PVEStarted(address owner, uint256 dungeonIndex, uint256 warriorId, uint256 battleEndBlock);

    /** @dev PVEFinished event. Emitted every time a warrior finishes pve battle
     *  @param owner Warrior owner
     *  @param dungeonIndex Finished dungeon index
     *  @param warriorId Warrior ID that completed dungeon
     *  @param cooldownEndBlock Block number, when cooldown on PVE battle entrance will be over
     *  @param rewardId Warrior ID which was granted to the owner as battle reward
     */
    event PVEFinished(address owner, uint256 dungeonIndex, uint256 warriorId, uint256 cooldownEndBlock, uint256 rewardId);

	/// @dev Update the address of the sanctuary contract, can only be called by the Admin.
    /// @param _address An address of a sanctuary contract instance to be used from this point forward.
    function setSanctuaryAddress(address _address) external onlyAdmin {
        SanctuaryInterface candidateContract = SanctuaryInterface(_address);

        // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
        require(candidateContract.isSanctuary());

        // Set the new contract address
        sanctuary = candidateContract;
    }
    
    function areUnique(uint256[] memory _warriorIds) internal pure returns(bool) {
   	    uint256 length = _warriorIds.length;
   	    uint256 j;
        for(uint256 i = 0; i < length; i++) {
	        for(j = i + 1; j < length; j++) {
	            if (_warriorIds[i] == _warriorIds[j]) return false;
	        }
        }
        return true; 
   	}

    /// @dev Updates the minimum payment required for calling startPVE(). Can only
    ///  be called by the COO address.
    function setPVEBattleFee(uint256 _pveBattleFee) external onlyAdmin {
        require(_pveBattleFee > PVE_COMPENSATION);
        pveBattleFee = _pveBattleFee;
    }
    
    /** @dev Returns PVE cooldown, after each battle, the warrior receives a 
     *  cooldown on the next entrance to the battle, cooldown depends on current warrior level,
     *  which is multiplied by 1h. Special case: after receiving 25 lv, the cooldwon will be 14 days.
     *  @param _levelPoints warrior level */
    function getPVECooldown(uint256 _levelPoints) public pure returns (uint256) {
        uint256 level = CryptoUtils._getLevel(_levelPoints);
        if (level >= MAX_LEVEL) return (14 * 24 * PVE_COOLDOWN);//14 days
        return (PVE_COOLDOWN * level);
    }

    /** @dev Returns PVE duration, each battle have a duration, which depends on current warrior level,
     *  which is multiplied by 15 min. At the end of the duration, warrior is becoming eligible to receive
     *  battle reward (new warrior in shiny armor)
     *  @param _levelPoints warrior level points 
     */
    function getPVEDuration(uint256 _levelPoints) public pure returns (uint256) {
        return CryptoUtils._getLevel(_levelPoints) * PVE_DURATION;
    }
    
    /// @dev Checks that a given warrior can participate in PVE battle. Requires that the
    ///  current cooldown is finished and also checks that warrior is idle (does not participate in any action)
    ///  and dungeon level requirement is satisfied
    function _isReadyToPVE(DataTypes.Warrior _warrior) internal view returns (bool) {
        return (_warrior.action == IDLE) && //is idle
        (_warrior.cooldownEndBlock <= uint64(block.number)) && //no cooldown
        (_warrior.level >= dungeonRequirements[_warrior.dungeonIndex]);//dungeon level requirement is satisfied
    }
    
    /// @dev Internal utility function to initiate pve battle, assumes that all battle
    ///  requirements have been checked.
    function _triggerPVEStart(uint256 _warriorId) internal {
        // Grab a reference to the warrior from storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        // Set warrior current action to pve battle
        warrior.action = uint16(PVE_BATTLE);
        // Set battle duration
        warrior.cooldownEndBlock = uint64((getPVEDuration(warrior.level) / secondsPerBlock) + block.number);
        // Emit the pve battle start event.
        PVEStarted(msg.sender, warrior.dungeonIndex, _warriorId, warrior.cooldownEndBlock);
    }
    
    /// @dev Starts PVE battle for specified warrior, 
    /// after battle, warrior owner will receive reward (Warrior) 
    /// @param _warriorId A Warrior ready to PVE battle.
    function startPVE(uint256 _warriorId) external payable whenNotPaused {
		// Checks for payment.
        require(msg.value >= pveBattleFee);
		
		// Caller must own the warrior.
        require(_ownerApproved(msg.sender, _warriorId));

        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];

        // Check that the warrior exists.
        require(warrior.identity != 0);

        // Check that the warrior is ready to battle
        require(_isReadyToPVE(warrior));
        
        // All checks passed, let the battle begin!
        _triggerPVEStart(_warriorId);
        
        // Calculate any excess funds included in msg.value. If the excess
        // is anything worth worrying about, transfer it back to message owner.
        // NOTE: We checked above that the msg.value is greater than or
        // equal to the price so this cannot underflow.
        uint256 feeExcess = msg.value - pveBattleFee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of _isReadyToPVE check
        // will fail
        msg.sender.transfer(feeExcess);
        //send battle fee to beneficiary
        bankAddress.transfer(pveBattleFee - PVE_COMPENSATION);
    }
    
    function _ariseWarrior(address _owner, DataTypes.Warrior storage _warrior) internal returns(uint256) {
        uint256 identity = sanctuary.generateWarrior(_warrior.identity, CryptoUtils._getLevel(_warrior.level), _warrior.cooldownEndBlock - 1, 0);
        return _createWarrior(identity, _owner, block.number + (PVE_COOLDOWN * SUMMONING_SICKENESS / secondsPerBlock), 10, 100, 0);
    }

	/// @dev Internal utility function to finish pve battle, assumes that all battle
    ///  finish requirements have been checked.
    function _triggerPVEFinish(uint256 _warriorId) internal {
        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        
        // Set warrior current action to idle
        warrior.action = uint16(IDLE);
        
        // Compute an estimation of the cooldown time in blocks (based on current level).
        // and miner perc also reduces cooldown time by 4 times
        warrior.cooldownEndBlock = uint64((getPVECooldown(warrior.level) / 
            CryptoUtils._getBonus(warrior.identity) / secondsPerBlock) + block.number);
        
        // cash completed dungeon index before increment
        uint256 dungeonIndex = warrior.dungeonIndex;
        // Increment the dungeon index, clamping it at 5, which is the length of the
        // dungeonRequirements array. We could check the array size dynamically, but hard-coding
        // this as a constant saves gas.
        if (dungeonIndex < 5) {
            warrior.dungeonIndex += 1;
        }
        
        address owner = warriorToOwner[_warriorId];
        // generate reward
        uint256 arisenWarriorId = _ariseWarrior(owner, warrior);
        //Emit event
        PVEFinished(owner, dungeonIndex, _warriorId, warrior.cooldownEndBlock, arisenWarriorId);
    }
    
    /**
     * @dev finishPVE can be called after battle time is over,
     * if checks are passed then battle result is computed,
     * and new warrior is awarded to owner of specified _warriord ID.
     * NB anyone can call this method, if they willing to pay the gas price
     */
    function finishPVE(uint256 _warriorId) external whenNotPaused {
        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        
        // Check that the warrior exists.
        require(warrior.identity != 0);
        
        // Check that warrior participated in PVE battle action
        require(warrior.action == PVE_BATTLE);
        
        // And the battle time is over
        require(warrior.cooldownEndBlock <= uint64(block.number));
        
        // When the all checks done, calculate actual battle result
        _triggerPVEFinish(_warriorId);
        
        //not susceptible to reetrance attack because of require(warrior.action == PVE_BATTLE)
        //and require(warrior.cooldownEndBlock <= uint64(block.number));
        msg.sender.transfer(PVE_COMPENSATION);
    }
    
    /**
     * @dev finishPVEBatch same as finishPVE but for multiple warrior ids.
     * NB anyone can call this method, if they willing to pay the gas price
     */
    function finishPVEBatch(uint256[] _warriorIds) external whenNotPaused {
        uint256 length = _warriorIds.length;
        //check max number of bach finish pve
        require(length <= 20);
        uint256 blockNumber = block.number;
        uint256 index;
        //all warrior ids must be unique
        require(areUnique(_warriorIds));
        //check prerequisites
        for(index = 0; index < length; index ++) {
            DataTypes.Warrior storage warrior = warriors[_warriorIds[index]];
			require(
		        // Check that the warrior exists.
			    warrior.identity != 0 &&
		        // Check that warrior participated in PVE battle action
			    warrior.action == PVE_BATTLE &&
		        // And the battle time is over
			    warrior.cooldownEndBlock <= blockNumber
			);
        }
        // When the all checks done, calculate actual battle result
        for(index = 0; index < length; index ++) {
            _triggerPVEFinish(_warriorIds[index]);
        }
        
        //not susceptible to reetrance attack because of require(warrior.action == PVE_BATTLE)
        //and require(warrior.cooldownEndBlock <= uint64(block.number));
        msg.sender.transfer(PVE_COMPENSATION * length);
    }
}

contract CryptoWarriorSanctuary is CryptoWarriorPVE {
    
    uint256 internal constant RARE = 3;
    
    function burnWarrior(uint256 _warriorId, address _owner) whenNotPaused external {
        require(msg.sender == address(sanctuary));
        
        // Caller must own the warrior.
        require(_ownerApproved(_owner, _warriorId));

        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];

        // Check that the warrior exists.
        require(warrior.identity != 0);

        // Check that the warrior is ready to battle
        require(warrior.action == IDLE);//is idle
        
        // Rarity of burned warrior must be less or equal RARE (3)
        require(CryptoUtils.getRarityValue(warrior.identity) <= RARE);
        // Warriors with MINER perc are not allowed to be berned
        require(CryptoUtils.getSpecialityValue(warrior.identity) < MINER_PERK);
        
        _burn(_owner, _warriorId);
    }
    
    function ariseWarrior(uint256 _identity, address _owner, uint256 _cooldown) whenNotPaused external returns(uint256){
        require(msg.sender == address(sanctuary));
        return _createWarrior(_identity, _owner, _cooldown, 10, 100, 0);
    }
    
}

contract CryptoWarriorPVP is CryptoWarriorSanctuary {
	
	PVPInterface public battleProvider;
	
	/// @dev Sets the reference to the sale auction.
    /// @param _address - Address of sale contract.
    function setBattleProviderAddress(address _address) external onlyAdmin {
        PVPInterface candidateContract = PVPInterface(_address);

        // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
        require(candidateContract.isPVPProvider());

        // Set the new contract address
        battleProvider = candidateContract;
    }
    
    function _packPVPData(uint256 _warriorId, DataTypes.Warrior storage warrior) internal view returns(uint256){
        return CryptoUtils._packWarriorPvpData(warrior.identity, uint256(warrior.rating), 0, _warriorId, warrior.level);
    }
    
    function _triggerPVPSignUp(uint256 _warriorId, uint256 fee) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
    		
		uint256 packedWarrior = _packPVPData(_warriorId, warrior);
        
        // addPVPContender will throw if fee fails.
        battleProvider.addPVPContender.value(fee)(msg.sender, packedWarrior);
        
        warrior.action = uint16(PVP_BATTLE);
    }
    
    /*
     * @title signUpForPVP enqueues specified warrior to PVP
     * 
     * @dev When the owner enqueues his warrior for PvP, the warrior enters the waiting room.
     * Once every 15 minutes, we check the warriors in the room and select pairs. 
     * For those warriors to whom we found couples, fighting is conducted and the results 
     * are recorded in the profile of the warrior. 
     */
    function signUpForPVP(uint256 _warriorId) public payable whenNotPaused {//done
		// Caller must own the warrior.
        require(_ownerApproved(msg.sender, _warriorId));
        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        // sanity check
        require(warrior.identity != 0);

        // Check that the warrior is ready to battle
        require(warrior.action == IDLE);
        
        // Define the current price of the auction.
        uint256 fee = battleProvider.getPVPEntranceFee(warrior.level);
        
        // Checks for payment.
        require(msg.value >= fee);
        
        // All checks passed, put the warrior to the queue!
        _triggerPVPSignUp(_warriorId, fee);
        
        // Calculate any excess funds included in msg.value. If the excess
        // is anything worth worrying about, transfer it back to message owner.
        // NOTE: We checked above that the msg.value is greater than or
        // equal to the price so this cannot underflow.
        uint256 feeExcess = msg.value - fee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of warrior.action == IDLE check
        // will fail
        msg.sender.transfer(feeExcess);
    }

    function _grandPVPWinnerReward(uint256 _warriorId) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        // reward 1 level, add 10 level points
        uint256 level = warrior.level;
        if (level < (MAX_LEVEL * POINTS_TO_LEVEL)) {
            level = level + POINTS_TO_LEVEL;
			warrior.level = uint64(level > (MAX_LEVEL * POINTS_TO_LEVEL) ? (MAX_LEVEL * POINTS_TO_LEVEL) : level);
        }
		// give 100 rating for levelUp and 30 for win
		warrior.rating += 130;
		// mark warrior idle, so it can participate
		// in another actions
		warrior.action = uint16(IDLE);
    }

    function _grandPVPLoserReward(uint256 _warriorId) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
		// reward 0.5 level
		uint256 oldLevel = warrior.level;
		uint256 level = oldLevel;
		if (level < (MAX_LEVEL * POINTS_TO_LEVEL)) {
            level += (POINTS_TO_LEVEL / 2);
			warrior.level = uint64(level);
        }
		// give 100 rating for levelUp if happens and -30 for lose
		int256 newRating = warrior.rating + (CryptoUtils._getLevel(level) > CryptoUtils._getLevel(oldLevel) ? int256(100 - 30) : int256(-30));
		// rating can't be less than 0 and more than 1000000000
	    warrior.rating = int64((newRating >= 0) ? (newRating > 1000000000 ? 1000000000 : newRating) : 0);
        // mark warrior idle, so it can participate
		// in another actions
	    warrior.action = uint16(IDLE);
    }
    
    function _grandPVPRewards(uint256[] memory warriorsData, uint256 matchingCount) internal {
        for(uint256 id = 0; id < matchingCount; id += 2){
            //
            // winner, even ids are winners!
            _grandPVPWinnerReward(CryptoUtils._unpackIdValue(warriorsData[id]));
            //
            // loser, they are odd...
            _grandPVPLoserReward(CryptoUtils._unpackIdValue(warriorsData[id + 1]));
        }
	}

    // @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function pvpFinished(uint256[] warriorsData, uint256 matchingCount) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        
        _grandPVPRewards(warriorsData, matchingCount);
    }
    
    function pvpContenderRemoved(uint256 _warriorId) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        //grab warrior storage reference
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        //specified warrior must be in pvp state
        require(warrior.action == PVP_BATTLE);
        //all checks done
        //set warrior state to IDLE
        warrior.action = uint16(IDLE);
    }
}

contract CryptoWarriorTournament is CryptoWarriorPVP {
    
    uint256 internal constant GROUP_SIZE = 5;
    
    function _ownsAll(address _claimant, uint256[] memory _warriorIds) internal view returns (bool) {
        uint256 length = _warriorIds.length;
        for(uint256 i = 0; i < length; i++) {
            if (!_ownerApproved(_claimant, _warriorIds[i])) return false;
        }
        return true;    
    }
    
    function _isReadyToTournament(DataTypes.Warrior storage _warrior) internal view returns(bool){
        return _warrior.level >= 50 && _warrior.action == IDLE;//must not participate in any action
    }
    
    function _packTournamentData(uint256[] memory _warriorIds) internal view returns(uint256[] memory tournamentData) {
        tournamentData = new uint256[](GROUP_SIZE);
        uint256 warriorId;
        for(uint256 i = 0; i < GROUP_SIZE; i++) {
            warriorId = _warriorIds[i];
            tournamentData[i] = _packPVPData(warriorId, warriors[warriorId]);   
        }
        return tournamentData;
    }
    
    
    // @dev Internal utility function to sign up to tournament, 
    // assumes that all battle requirements have been checked.
    function _triggerTournamentSignUp(uint256[] memory _warriorIds, uint256 fee) internal {
        //pack warrior ids into into uint256
        uint256[] memory tournamentData = _packTournamentData(_warriorIds);
        
        for(uint256 i = 0; i < GROUP_SIZE; i++) {
            // Set warrior current action to tournament battle
            warriors[_warriorIds[i]].action = uint16(TOURNAMENT_BATTLE);
        }

        battleProvider.addTournamentContender.value(fee)(msg.sender, tournamentData);
    }
    
    function signUpForTournament(uint256[] _warriorIds) public payable {
        //
        //check that there is enough funds to pay entrance fee
        uint256 fee = battleProvider.getTournamentThresholdFee();
        require(msg.value >= fee);
        //
        //check that warriors group is exactly of allowed size
        require(_warriorIds.length == GROUP_SIZE);
        //
        //message sender must own all the specified warrior IDs
        require(_ownsAll(msg.sender, _warriorIds));
        //
        //check all warriors are unique
        require(areUnique(_warriorIds));
        //
        //check that all warriors are 25 lv and IDLE
        for(uint256 i = 0; i < GROUP_SIZE; i ++) {
            // Grab a reference to the warrior in storage.
            require(_isReadyToTournament(warriors[_warriorIds[i]]));
        }
        
        
        //all checks passed, trigger sign up
        _triggerTournamentSignUp(_warriorIds, fee);
        
        // Calculate any excess funds included in msg.value. If the excess
        // is anything worth worrying about, transfer it back to message owner.
        // NOTE: We checked above that the msg.value is greater than or
        // equal to the fee so this cannot underflow.
        uint256 feeExcess = msg.value - fee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of _isReadyToTournament check
        // will fail
        msg.sender.transfer(feeExcess);
    }
    
    function _setIDLE(uint256 warriorIds) internal {
        for(uint256 i = 0; i < GROUP_SIZE; i ++) {
            warriors[CryptoUtils._unpackWarriorId(warriorIds, i)].action = uint16(IDLE);
        }
    }
    
    function _freeWarriors(uint256[] memory packedContenders) internal {
        uint256 length = packedContenders.length;
        for(uint256 i = 0; i < length; i ++) {
            //set participants action to IDLE
            _setIDLE(packedContenders[i]);
        }
    }
    
    function tournamentFinished(uint256[] packedContenders) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        
        //grad rewards and set IDLE action
        _freeWarriors(packedContenders);
    }
    
}

contract CryptoWarriorAuction is CryptoWarriorTournament {

    // @notice The auction contract variables are defined in CryptoWarriorBase to allow
    //  us to refer to them in WarriorTokenImpl to prevent accidental transfers.
    // `saleAuction` refers to the auction for miner and p2p sale of warriors.

    /// @dev Sets the reference to the sale auction.
    /// @param _address - Address of sale contract.
    function setSaleAuctionAddress(address _address) external onlyAdmin {
        SaleClockAuction candidateContract = SaleClockAuction(_address);

        // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
        require(candidateContract.isSaleClockAuction());

        // Set the new contract address
        saleAuction = candidateContract;
    }


    /// @dev Put a warrior up for auction.
    ///  Does some ownership trickery to create auctions in one tx.
    function createSaleAuction(
        uint256 _warriorId,
        uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration
    )
        external
        whenNotPaused
    {
        // Auction contract checks input sizes
        // If warrior is already on any auction, this will throw
        // because it will be owned by the auction contract.
        require(_ownerApproved(msg.sender, _warriorId));
        // Ensure the warrior is not busy to prevent the auction
        // contract creation while warrior is in any kind of battle (PVE, PVP, TOURNAMENT).
        require(warriors[_warriorId].action == IDLE);
        _approve(_warriorId, address(saleAuction));
        // Sale auction throws if inputs are invalid and clears
        // transfer approval after escrowing the warrior.
        saleAuction.createAuction(
            _warriorId,
            _startingPrice,
            _endingPrice,
            _duration,
            msg.sender
        );
    }

}

contract CryptoWarriorIssuer is CryptoWarriorAuction {
    
    // Limits the number of warriors the contract owner can ever create
    uint256 public constant MINER_CREATION_LIMIT = 2880;//issue every 15min for one month
    // Constants for miner auctions.
    uint256 public constant MINER_STARTING_PRICE = 100 finney;
    uint256 public constant MINER_END_PRICE = 50 finney;
    uint256 public constant MINER_AUCTION_DURATION = 1 days;

    uint256 public minerCreatedCount;

    /// @dev Generates a new miner warrior with MINER perk of COMMON rarity
    ///  creates an auction for it.
    function createMinerAuction() external onlyIssuer {
        require(minerCreatedCount < MINER_CREATION_LIMIT);
		
        minerCreatedCount++;

        uint256 identity = sanctuary.generateWarrior(minerCreatedCount, 0, block.number - 1, MINER_PERK);
        uint256 warriorId = _createWarrior(identity, bankAddress, 0, 10, 100, 0);
        _approve(warriorId, address(saleAuction));

        saleAuction.createAuction(
            warriorId,
            _computeNextMinerPrice(),
            MINER_END_PRICE,
            MINER_AUCTION_DURATION,
            bankAddress
        );
    }

    /// @dev Computes the next miner auction starting price, given
    ///  the average of the past 5 prices * 2.
    function _computeNextMinerPrice() internal view returns (uint256) {
        uint256 avePrice = saleAuction.averageMinerSalePrice();

        // Sanity check to ensure we don't overflow arithmetic
        require(avePrice == uint256(uint128(avePrice)));

        uint256 nextPrice = avePrice * 3 / 2;//confirmed

        // We never auction for less than starting price
        if (nextPrice < MINER_STARTING_PRICE) {
            nextPrice = MINER_STARTING_PRICE;
        }

        return nextPrice;
    }

}

contract CoreRecovery is CryptoWarriorIssuer {
    
    bool public allowRecovery = true;
    
    //data model
    //0 - identity
    //1 - cooldownEndBlock
    //2 - level
    //3 - rating
    //4 - index
    function recoverWarriors(uint256[] recoveryData, address[] owners) external onlyAdmin whenPaused {
        //check that recory action is allowed
        require(allowRecovery);
        
        uint256 length = owners.length;
        
        //check that number of owners corresponds to recover data length
        require(length == recoveryData.length / 5);
        
        for(uint256 i = 0; i < length; i++) {
            _createWarrior(recoveryData[i * 5], owners[i], recoveryData[i * 5 + 1], 
                recoveryData[i * 5 + 2], recoveryData[i * 5 + 3], recoveryData[i * 5 + 4]);
        }
    }
    
    //recovery is a one time action, once it is done no more recovery actions allowed
    function recoveryDone() external onlyAdmin {
        allowRecovery = false;
    }

}

contract CryptoWarriorCore is CoreRecovery {

    /// @notice Creates the main CryptoWarrior smart contract instance.
    function CryptoWarriorCore() public {
        // Starts paused.
        paused = true;

        // the creator of the contract is the initial Admin
        adminAddress = msg.sender;

        // the creator of the contract is also the initial COO
        issuerAddress = msg.sender;
        
        // the creator of the contract is also the initial Bank
        bankAddress = msg.sender;
    }
    
    /// @notice No tipping!
    /// @dev Reject all Ether from being sent here
    /// (Hopefully, we can prevent user accidents.)
    function() external payable {
        require(false);
    }
    
    /// @dev Override unpause so it requires all external contract addresses
    ///  to be set before contract can be unpaused. Also, we can't have
    ///  newContractAddress set either, because then the contract was upgraded.
    /// @notice This is public rather than external so we can call super.unpause
    ///  without using an expensive CALL.
    function unpause() public onlyAdmin whenPaused {
        require(address(saleAuction) != address(0));
        require(address(sanctuary) != address(0));
        require(address(battleProvider) != address(0));
        require(newContractAddress == address(0));

        // Actually unpause the contract.
        super.unpause();
    }
    
    function getBeneficiary() external view returns(address) {
        return bankAddress;
    }
    
    function isPVPListener() public pure returns (bool) {
        return true;
    }
       
    /**
     *@param _warriorIds array of warriorIds, 
     * for those IDs warrior data will be packed into warriorsData array
     *@return warriorsData packed warrior data
     *@return stepSize number of fields in single warrior data */
    function getWarriors(uint256[] _warriorIds) external view returns (uint256[] memory warriorsData, uint256 stepSize) {
        stepSize = 6;
        warriorsData = new uint256[](_warriorIds.length * stepSize);
        for(uint256 i = 0; i < _warriorIds.length; i++) {
            _setWarriorData(warriorsData, warriors[_warriorIds[i]], i * stepSize);
        }
    }
    
    /**
     *@param indexFrom index in global warrior storage (aka warriorId), 
     * from this index(including), warriors data will be gathered
     *@param count Number of warriors to include in packed data
     *@return warriorsData packed warrior data
     *@return stepSize number of fields in single warrior data */
    function getWarriorsFromIndex(uint256 indexFrom, uint256 count) external view returns (uint256[] memory warriorsData, uint256 stepSize) {
        stepSize = 6;
        //check length
        uint256 lenght = (warriors.length - indexFrom >= count ? count : warriors.length - indexFrom);
        
        warriorsData = new uint256[](lenght * stepSize);
        for(uint256 i = 0; i < lenght; i ++) {
            _setWarriorData(warriorsData, warriors[indexFrom + i], i * stepSize);
        }
    }
    
    function getWarriorOwners(uint256[] _warriorIds) external view returns (address[] memory owners) {
        uint256 lenght = _warriorIds.length;
        owners = new address[](lenght);
        
        for(uint256 i = 0; i < lenght; i ++) {
            owners[i] = warriorToOwner[_warriorIds[i]];
        }
    }
    
    
    function _setWarriorData(uint256[] memory warriorsData, DataTypes.Warrior storage warrior, uint256 id) internal view {
        warriorsData[id] = uint256(warrior.identity);//0
        warriorsData[id + 1] = uint256(warrior.cooldownEndBlock);//1
        warriorsData[id + 2] = uint256(warrior.level);//2
        warriorsData[id + 3] = uint256(warrior.rating);//3
        warriorsData[id + 4] = uint256(warrior.action);//4
        warriorsData[id + 5] = uint256(warrior.dungeonIndex);//5
    }
    
	function getWarrior(uint256 _id) external view returns 
    (
        uint256 identity, 
        uint256 cooldownEndBlock, 
        uint256 level,
        uint256 rating, 
        uint256 action,
        uint256 dungeonIndex
    ) {
        DataTypes.Warrior storage warrior = warriors[_id];

        identity = uint256(warrior.identity);
        cooldownEndBlock = uint256(warrior.cooldownEndBlock);
        level = uint256(warrior.level);
		rating = uint256(warrior.rating);
		action = uint256(warrior.action);
		dungeonIndex = uint256(warrior.dungeonIndex);
    }
    
}

/*  @title Handles creating pvp battles every 15 min.*/
contract PVP is PausableBattle, PVPInterface {
	/* PVP BATLE */
	
    /** list of packed warrior data that will participate in next PVP session. 
     *  Fixed size arry, to evade constant remove and push operations,
     *  this approach reduces transaction costs involving queue modification. */
    uint256[100] public pvpQueue;
    //
    //queue size
    uint256 public pvpQueueSize = 0;
    
    // @dev A mapping from owner address to booty in WEI
    //  booty is acquired in PVP and Tournament battles and can be
    // withdrawn with grabBooty method by the owner of the loot
    mapping (address => uint256) public ownerToBooty;
    
    // @dev A mapping from warrior id to owners address
    mapping (uint256 => address) internal warriorToOwner;
    
    // An approximation of currently how many seconds are in between blocks.
    uint256 internal secondsPerBlock = 15;
    
    // Cut owner takes from, measured in basis points (1/100 of a percent).
    // Values 0-10,000 map to 0%-100%
    uint256 public pvpOwnerCut;
    
    // Values 0-10,000 map to 0%-100%
    //this % of the total bets will be sent as 
    //a reward to address, that triggered finishPVP method
    uint256 public pvpMaxIncentiveCut;
    
    /// @notice The payment base required to use startPVP().
    // pvpBattleFee * (warrior.level / POINTS_TO_LEVEL)
    uint256 internal pvpBattleFee = 10 finney;
    
    uint256 public constant PVP_INTERVAL = 15 minutes;
    
    uint256 public nextPVPBatleBlock = 0;
    //number of WEI in hands of warrior owners
    uint256 public totalBooty = 0;
    
    /* TOURNAMENT */
    uint256 public constant FUND_GATHERING_TIME = 24 hours;
    uint256 public constant ADMISSION_TIME = 12 hours;
    uint256 public constant RATING_EXPAND_INTERVAL = 1 hours;
    uint256 internal constant SAFETY_GAP = 5;
    
    uint256 internal constant MAX_INCENTIVE_REWARD = 200 finney;
    
    //tournamentContenders size
    uint256 public tournamentQueueSize = 0;
    
    // Values 0-10,000 map to 0%-100%
    uint256 public tournamentBankCut;
    
   /** tournamentEndBlock, tournament is eligible to be finished only
    *  after block.number >= tournamentEndBlock 
    *  it depends on FUND_GATHERING_TIME and ADMISSION_TIME */
    uint256 public tournamentEndBlock;
    
    //number of WEI in tournament bank
    uint256 public currentTournamentBank = 0;
    uint256 public nextTournamentBank = 0;
    
    PVPListenerInterface internal pvpListener;
    
    /* EVENTS */
    /** @dev TournamentScheduled event. Emitted every time a tournament is scheduled 
     *  @param tournamentEndBlock when block.number > tournamentEndBlock, then tournament 
     *         is eligible to be finished or rescheduled */
    event TournamentScheduled(uint256 tournamentEndBlock);
    
    /** @dev PVPScheduled event. Emitted every time a tournament is scheduled 
     *  @param nextPVPBatleBlock when block.number > nextPVPBatleBlock, then pvp battle 
     *         is eligible to be finished or rescheduled */
    event PVPScheduled(uint256 nextPVPBatleBlock);
    
    /** @dev PVPNewContender event. Emitted every time a warrior enqueues pvp battle
     *  @param owner Warrior owner
     *  @param warriorId Warrior ID that entered PVP queue
     *  @param entranceFee fee in WEI warrior owner payed to enter PVP
     */
    event PVPNewContender(address owner, uint256 warriorId, uint256 entranceFee);

    /** @dev PVPFinished event. Emitted every time a pvp battle is finished
     *  @param warriorsData array of pairs of pvp warriors packed to uint256, even => winners, odd => losers 
     *  @param owners array of warrior owners, 1 to 1 with warriorsData, even => winners, odd => losers 
     *  @param matchingCount total number of warriors that fought in current pvp session and got rewards,
     *  if matchingCount < participants.length then all IDs that are >= matchingCount will 
     *  remain in waiting room, until they are matched.
     */
    event PVPFinished(uint256[] warriorsData, address[] owners, uint256 matchingCount);
    
    /** @dev BootySendFailed event. Emitted every time address.send() function failed to transfer Ether to recipient
     *  in this case recipient Ether is recorded to ownerToBooty mapping, so recipient can withdraw their booty manually
     *  @param recipient address for whom send failed
     *  @param amount number of WEI we failed to send
     */
    event BootySendFailed(address recipient, uint256 amount);
    
    /** @dev BootyGrabbed event
     *  @param receiver address who grabbed his booty
     *  @param amount number of WEI
     */
    event BootyGrabbed(address receiver, uint256 amount);
    
    /** @dev PVPContenderRemoved event. Emitted every time warrior is removed from pvp queue by its owner.
     *  @param warriorId id of the removed warrior
     */
    event PVPContenderRemoved(uint256 warriorId, address owner);
    
    function PVP(uint256 _pvpCut, uint256 _tournamentBankCut, uint256 _pvpMaxIncentiveCut) public {
        require((_tournamentBankCut + _pvpCut + _pvpMaxIncentiveCut) <= 10000);
		pvpOwnerCut = _pvpCut;
		tournamentBankCut = _tournamentBankCut;
		pvpMaxIncentiveCut = _pvpMaxIncentiveCut;
    }
    
    /** @dev grabBooty sends to message sender his booty in WEI
     */
    function grabBooty() external {
        uint256 booty = ownerToBooty[msg.sender];
        require(booty > 0);
        require(totalBooty >= booty);
        
        ownerToBooty[msg.sender] = 0;
        totalBooty -= booty;
        
        msg.sender.transfer(booty);
        //emit event
        BootyGrabbed(msg.sender, booty);
    }
    
    function safeSend(address _recipient, uint256 _amaunt) internal {
		uint256 failedBooty = sendBooty(_recipient, _amaunt);
        if (failedBooty > 0) {
			totalBooty += failedBooty;
        }
    }
    
    function sendBooty(address _recipient, uint256 _amaunt) internal returns(uint256) {
        bool success = _recipient.send(_amaunt);
        if (!success && _amaunt > 0) {
            ownerToBooty[_recipient] += _amaunt;
            BootySendFailed(_recipient, _amaunt);
            return _amaunt;
        }
        return 0;
    }
    
    //@returns block number, after this block tournament is opened for admission
    function getTournamentAdmissionBlock() public view returns(uint256) {
        uint256 admissionInterval = (ADMISSION_TIME / secondsPerBlock);
        return tournamentEndBlock < admissionInterval ? 0 : tournamentEndBlock - admissionInterval;
    }
    
    
    //schedules next turnament time(block)
    function _scheduleTournament() internal {
        //we can chedule only if there is nobody in tournament queue and
        //time of tournament battle have passed
		if (tournamentQueueSize == 0 && tournamentEndBlock <= block.number) {
		    tournamentEndBlock = ((FUND_GATHERING_TIME / 2 + ADMISSION_TIME) / secondsPerBlock) + block.number;
		    TournamentScheduled(tournamentEndBlock);
		}
    }
    
    /// @dev Updates the minimum payment required for calling startPVP(). Can only
    ///  be called by the COO address, and only if pvp queue is empty.
    function setPVPEntranceFee(uint256 value) external onlyOwner {
        require(pvpQueueSize == 0);
        pvpBattleFee = value;
    }
    
    //@returns PVP entrance fee for specified warrior level 
    //@param _levelPoints NB!
    function getPVPEntranceFee(uint256 _levelPoints) external view returns(uint256) {
        return pvpBattleFee * CryptoUtils._getLevel(_levelPoints);
    }
    
    //level can only be > 0 and <= 25
    function _getPVPFeeByLevel(uint256 _level) internal view returns(uint256) {
        return pvpBattleFee * _level;
    }
    
	// @dev Computes warrior pvp reward
    // @param _totalBet - total bet from both competitors.
    function _computePVPReward(uint256 _totalBet, uint256 _contendersCut) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalBet max value is 1000 finney, and _contendersCut aka
        // (10000 - pvpOwnerCut - tournamentBankCut - incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalBet.
        return _totalBet * _contendersCut / 10000;
    }
    
    function _getPVPContendersCut(uint256 _incentiveCut) internal view returns (uint256) {
        // NOTE: We don't use SafeMath (or similar) in this function because
        // (pvpOwnerCut + tournamentBankCut + pvpMaxIncentiveCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). 
        // _incentiveCut is guaranteed to be >= 1 and <=  pvpMaxIncentiveCut
        return (10000 - pvpOwnerCut - tournamentBankCut - _incentiveCut);
    }
	
	// @dev Computes warrior pvp reward
    // @param _totalSessionLoot - total bets from all competitors.
    function _computeIncentiveReward(uint256 _totalSessionLoot, uint256 _incentiveCut) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * _incentiveCut / 10000;
    }
    
	///@dev computes incentive cut for specified loot, 
	/// Values 0-10,000 map to 0%-100%
	/// max incentive reward cut is 5%, if it exceeds MAX_INCENTIVE_REWARD,
	/// then cut is lowered to be equal to MAX_INCENTIVE_REWARD.
	/// minimum cut is 0.01%
    /// this % of the total bets will be sent as 
    /// a reward to address, that triggered finishPVP method
    function _computeIncentiveCut(uint256 _totalSessionLoot, uint256 maxIncentiveCut) internal pure returns(uint256) {
        uint256 result = _totalSessionLoot * maxIncentiveCut / 10000;
        result = result <= MAX_INCENTIVE_REWARD ? maxIncentiveCut : MAX_INCENTIVE_REWARD * 10000 / _totalSessionLoot;
        //min cut is 0.01%
        return result > 0 ? result : 1;
    }
    
    // @dev Computes warrior pvp reward
    // @param _totalSessionLoot - total bets from all competitors.
    function _computePVPBeneficiaryFee(uint256 _totalSessionLoot) internal view returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * pvpOwnerCut / 10000;
    }
    
    // @dev Computes tournament bank cut
    // @param _totalSessionLoot - total session loot.
    function _computeTournamentCut(uint256 _totalSessionLoot) internal view returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because
        // _totalSessionLoot max value is 37500 finney, and 
        // (pvpOwnerCut + tournamentBankCut + incentiveRewardCut) <= 10000 (see the require()
        // statement in the BattleProvider constructor). The result of this
        // function is always guaranteed to be <= _totalSessionLoot.
        return _totalSessionLoot * tournamentBankCut / 10000;
    }

    function indexOf(uint256 _warriorId) internal view returns(int256) {
	    uint256 length = uint256(pvpQueueSize);
	    for(uint256 i = 0; i < length; i ++) {
	        if(CryptoUtils._unpackIdValue(pvpQueue[i]) == _warriorId) return int256(i);
	    }
	    return -1;
	}
    
    function getPVPIncentiveReward(uint256[] memory matchingIds, uint256 matchingCount) internal view returns(uint256) {
        uint256 sessionLoot = _computeTotalBooty(matchingIds, matchingCount);
        
        return _computeIncentiveReward(sessionLoot, _computeIncentiveCut(sessionLoot, pvpMaxIncentiveCut));
    }
    
    function maxPVPContenders() external view returns(uint256){
        return pvpQueue.length;
    }
    
    function getPVPState() external view returns
    (uint256 contendersCount, uint256 matchingCount, uint256 endBlock, uint256 incentiveReward)
    {
        uint256[] memory pvpData = _packPVPData();
        
    	contendersCount = pvpQueueSize;
    	matchingCount = CryptoUtils._getMatchingIds(pvpData, PVP_INTERVAL, _computeCycleSkip(), RATING_EXPAND_INTERVAL);
    	endBlock = nextPVPBatleBlock;   
    	incentiveReward = getPVPIncentiveReward(pvpData, matchingCount);
    }
    
    function canFinishPVP() external view returns(bool) {
        return nextPVPBatleBlock <= block.number &&
         CryptoUtils._getMatchingIds(_packPVPData(), PVP_INTERVAL, _computeCycleSkip(), RATING_EXPAND_INTERVAL) > 1;
    }
    
    function _clarifyPVPSchedule() internal {
        uint256 length = pvpQueueSize;
		uint256 currentBlock = block.number;
		uint256 nextBattleBlock = nextPVPBatleBlock;
		//if battle not scheduled, schedule battle
		if (nextBattleBlock <= currentBlock) {
		    //if queue not empty update cycles
		    if (length > 0) {
				uint256 packedWarrior;
				uint256 cycleSkip = _computeCycleSkip();
		        for(uint256 i = 0; i < length; i++) {
		            packedWarrior = pvpQueue[i];
		            //increase warrior iteration cycle
		            pvpQueue[i] = CryptoUtils._changeCycleValue(packedWarrior, CryptoUtils._unpackCycleValue(packedWarrior) + cycleSkip);
		        }
		    }
		    nextBattleBlock = (PVP_INTERVAL / secondsPerBlock) + currentBlock;
		    nextPVPBatleBlock = nextBattleBlock;
		    PVPScheduled(nextBattleBlock);
		//if pvp queue will be full and there is still too much time left, then let the battle begin! 
		} else if (length + 1 == pvpQueue.length && (currentBlock + SAFETY_GAP * 2) < nextBattleBlock) {
		    nextBattleBlock = currentBlock + SAFETY_GAP;
		    nextPVPBatleBlock = nextBattleBlock;
		    PVPScheduled(nextBattleBlock);
		}
    }
    
    /// @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function _triggerNewPVPContender(address _owner, uint256 _packedWarrior, uint256 fee) internal {

		_clarifyPVPSchedule();
        //number of pvp cycles the warrior is waiting for suitable enemy match
        //increment every time when finishPVP is called and no suitable enemy match was found
        _packedWarrior = CryptoUtils._changeCycleValue(_packedWarrior, 0);
		
		//record contender data
		pvpQueue[pvpQueueSize++] = _packedWarrior;
		warriorToOwner[CryptoUtils._unpackIdValue(_packedWarrior)] = _owner;
		
		//Emit event
		PVPNewContender(_owner, CryptoUtils._unpackIdValue(_packedWarrior), fee);
    }
    
    function _noMatchingPairs() internal view returns(bool) {
        uint256 matchingCount = CryptoUtils._getMatchingIds(_packPVPData(), uint64(PVP_INTERVAL), _computeCycleSkip(), uint64(RATING_EXPAND_INTERVAL));
        return matchingCount == 0;
    }
    
    /*
     * @title startPVP enqueues specified warrior to PVP
     * 
     * @dev When the owner enqueues his warrior for PvP, the warrior enters the waiting room.
     * Once every 15 minutes, we check the warriors in the room and select pairs. 
     * For those warriors to whom we found couples, fighting is conducted and the results 
     * are recorded in the profile of the warrior. 
     */
    function addPVPContender(address _owner, uint256 _packedWarrior) external payable PVPNotPaused {
		// Caller must be pvpListener contract
        require(msg.sender == address(pvpListener));

        require(_owner != address(0));
        //contender can be added only while PVP is scheduled in future
        //or no matching warrior pairs found
        require(nextPVPBatleBlock > block.number || _noMatchingPairs());
        // Check that the warrior exists.
        require(_packedWarrior != 0);
        //owner must withdraw all loot before contending pvp
        require(ownerToBooty[_owner] == 0);
        //check that there is enough room for new participants
        require(pvpQueueSize < pvpQueue.length);
        // Checks for payment.
        uint256 fee = _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarrior));
        require(msg.value >= fee);
        //
        // All checks passed, put the warrior to the queue!
        _triggerNewPVPContender(_owner, _packedWarrior, fee);
    }
    
    function _packPVPData() internal view returns(uint256[] memory matchingIds) {
        uint256 length = pvpQueueSize;
        matchingIds = new uint256[](length);
        for(uint256 i = 0; i < length; i++) {
            matchingIds[i] = pvpQueue[i];
        }
        return matchingIds;
    }
    
    function _computeTotalBooty(uint256[] memory _packedWarriors, uint256 matchingCount) internal view returns(uint256) {
        //compute session booty
        uint256 sessionLoot = 0;
        for(uint256 i = 0; i < matchingCount; i++) {
            sessionLoot += _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarriors[i]));
        }
        return sessionLoot;
    }
    
    function _grandPVPRewards(uint256[] memory _packedWarriors, uint256 matchingCount) 
    internal returns(uint256)
    {
        uint256 booty = 0;
        uint256 packedWarrior;
        uint256 failedBooty = 0;
        
        uint256 sessionBooty = _computeTotalBooty(_packedWarriors, matchingCount);
        uint256 incentiveCut = _computeIncentiveCut(sessionBooty, pvpMaxIncentiveCut);
        uint256 contendersCut = _getPVPContendersCut(incentiveCut);
        
        for(uint256 id = 0; id < matchingCount; id++) {
            //give reward to warriors that fought hard
			//winner, even ids are winners!
			packedWarrior = _packedWarriors[id];
			//
			//give winner deserved booty 80% from both bets
			//must be computed before level reward!
			booty = _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(packedWarrior)) + 
				_getPVPFeeByLevel(CryptoUtils._unpackLevelValue(_packedWarriors[id + 1]));
			
			//
			//send reward to warrior owner
			failedBooty += sendBooty(warriorToOwner[CryptoUtils._unpackIdValue(packedWarrior)], _computePVPReward(booty, contendersCut));
			//loser, they are odd...
			//skip them, as they deserve none!
			id ++;
        }
        failedBooty += sendBooty(pvpListener.getBeneficiary(), _computePVPBeneficiaryFee(sessionBooty));
        
        if (failedBooty > 0) {
            totalBooty += failedBooty;
        }
        //if tournament admission start time not passed
        //add tournament cut to current tournament bank,
        //otherwise to next tournament bank
        if (getTournamentAdmissionBlock() > block.number) {
            currentTournamentBank += _computeTournamentCut(sessionBooty);
        } else {
            nextTournamentBank += _computeTournamentCut(sessionBooty);
        }
        
        //compute incentive reward
        return _computeIncentiveReward(sessionBooty, incentiveCut);
    }
    
    function _increaseCycleAndTrimQueue(uint256[] memory matchingIds, uint256 matchingCount) internal {
        uint32 length = uint32(matchingIds.length - matchingCount);  
		uint256 packedWarrior;
		uint256 skipCycles = _computeCycleSkip();
        for(uint256 i = 0; i < length; i++) {
            packedWarrior = matchingIds[matchingCount + i];
            //increase warrior iteration cycle
            pvpQueue[i] = CryptoUtils._changeCycleValue(packedWarrior, CryptoUtils._unpackCycleValue(packedWarrior) + skipCycles);
        }
        //trim queue	
        pvpQueueSize = length;
    }
    
    function _computeCycleSkip() internal view returns(uint256) {
        uint256 number = block.number;
        return nextPVPBatleBlock > number ? 0 : (number - nextPVPBatleBlock) * secondsPerBlock / PVP_INTERVAL + 1;
    }
    
    function _getWarriorOwners(uint256[] memory pvpData) internal view returns (address[] memory owners){
        uint256 length = pvpData.length;
        owners = new address[](length);
        for(uint256 i = 0; i < length; i ++) {
            owners[i] = warriorToOwner[CryptoUtils._unpackIdValue(pvpData[i])];
        }
    }
    
    // @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function _triggerPVPFinish(uint256[] memory pvpData, uint256 matchingCount) internal returns(uint256){
        //
		//compute battle results        
        CryptoUtils._getPVPBattleResults(pvpData, matchingCount, nextPVPBatleBlock);
        //
        //mark not fought warriors and trim queue 
        _increaseCycleAndTrimQueue(pvpData, matchingCount);
        //
        //schedule next battle time
        nextPVPBatleBlock = (PVP_INTERVAL / secondsPerBlock) + block.number;
        
        //
        //schedule tournament
        //if contendersCount is 0 and tournament not scheduled, schedule tournament
        //NB MUST be before _grandPVPRewards()
        _scheduleTournament();
        // compute and grand rewards to warriors,
        // put tournament cut to bank, not susceptible to reentry attack because of require(nextPVPBatleBlock <= block.number);
        // and require(number of pairs > 1);
        uint256 incentiveReward = _grandPVPRewards(pvpData, matchingCount);
        //
        //notify pvp listener contract
        pvpListener.pvpFinished(pvpData, matchingCount);
        
        //
        //fire event
		PVPFinished(pvpData, _getWarriorOwners(pvpData), matchingCount);
        PVPScheduled(nextPVPBatleBlock);
		
		return incentiveReward;
    }
    
    
    /**
     * @dev finishPVP this method finds matches of warrior pairs
     * in waiting room and computes result of their fights.
     * 
     * The winner gets +1 level, the loser gets +0.5 level
     * The winning player gets +130 rating
	 * The losing player gets -30 or 70 rating (if warrior levelUps after battle) .
     * can be called once in 15min.
     * NB If the warrior is not picked up in an hour, then we expand the range 
     * of selection by 25 rating each hour.
     */
    function finishPVP() public PVPNotPaused {
        // battle interval is over
        require(nextPVPBatleBlock <= block.number);
        //
	    //match warriors
        uint256[] memory pvpData = _packPVPData();
        //match ids and sort them according to matching
        uint256 matchingCount = CryptoUtils._getMatchingIds(pvpData, uint64(PVP_INTERVAL), _computeCycleSkip(), uint64(RATING_EXPAND_INTERVAL));
		// we have at least 1 matching battle pair
        require(matchingCount > 1);
        
        // When the all checks done, calculate actual battle result
        uint256 incentiveReward = _triggerPVPFinish(pvpData, matchingCount);
        
        //give reward for incentive
        safeSend(msg.sender, incentiveReward);
    }

    // @dev Removes specified warrior from PVP queue
    //  sets warrior free (IDLE) and returns pvp entrance fee to owner
    // @notice This is a state-modifying function that can
    //  be called while the contract is paused.
    // @param _warriorId - ID of warrior in PVP queue
    function removePVPContender(uint256 _warriorId) external{
        uint256 queueSize = pvpQueueSize;
        require(queueSize > 0);
        // Caller must be owner of the specified warrior
        require(warriorToOwner[_warriorId] == msg.sender);
        //warrior must be in pvp queue
        int256 warriorIndex = indexOf(_warriorId);
        require(warriorIndex >= 0);
        //grab warrior data
        uint256 warriorData = pvpQueue[uint32(warriorIndex)];
        //warrior cycle must be >= 4 (> than 1 hour)
        require((CryptoUtils._unpackCycleValue(warriorData) + _computeCycleSkip()) >= 4);
        
        //remove from queue
        if (uint256(warriorIndex) < queueSize - 1) {
	        pvpQueue[uint32(warriorIndex)] = pvpQueue[pvpQueueSize - 1];
        }
        pvpQueueSize --;
        //notify battle listener
        pvpListener.pvpContenderRemoved(_warriorId);
        //return pvp bet
        msg.sender.transfer(_getPVPFeeByLevel(CryptoUtils._unpackLevelValue(warriorData)));
        //Emit event
        PVPContenderRemoved(_warriorId, msg.sender);
    }
    
    function getPVPCycles(uint32[] warriorIds) external view returns(uint32[]){
        uint256 length = warriorIds.length;
        uint32[] memory cycles = new uint32[](length);
        int256 index;
        uint256 skipCycles = _computeCycleSkip();
	    for(uint256 i = 0; i < length; i ++) {
	        index = indexOf(warriorIds[i]);
	        cycles[i] = index >= 0 ? uint32(CryptoUtils._unpackCycleValue(pvpQueue[uint32(index)]) + skipCycles) : 0;
	    }
	    return cycles;
    }
    
    // @dev Remove all PVP contenders from PVP queue 
    //  and return all bets to warrior owners.
    //  NB: this is emergency method, used only in f%#^@up situation
    function removeAllPVPContenders() external onlyOwner PVPPaused {
        //remove all pvp contenders
        uint256 length = pvpQueueSize;
        
        uint256 warriorData;
        uint256 warriorId;
        uint256 failedBooty;
        address owner;
        
        pvpQueueSize = 0;
        
        for(uint256 i = 0; i < length; i++) {
	        //grab warrior data
	        warriorData = pvpQueue[i];
	        warriorId = CryptoUtils._unpackIdValue(warriorData);
	        //notify battle listener
	        pvpListener.pvpContenderRemoved(uint32(warriorId));
	        
	        owner = warriorToOwner[warriorId];
	        //return pvp bet
	        failedBooty += sendBooty(owner, _getPVPFeeByLevel(CryptoUtils._unpackLevelValue(warriorData)));
        }
        totalBooty += failedBooty;
    }
}


contract Tournament is PVP {

    uint256 internal constant GROUP_SIZE = 5;
    uint256 internal constant DATA_SIZE = 2;
    uint256 internal constant THRESHOLD = 300;
    
  /** list of warrior IDs that will participate in next tournament. 
    *  Fixed size arry, to evade constant remove and push operations,
    *  this approach reduces transaction costs involving array modification. */
    uint256[160] public tournamentQueue;
    
    /**The cost of participation in the tournament is 1% of its current prize fund, 
     * money is added to the prize fund. measured in basis points (1/100 of a percent).
     * Values 0-10,000 map to 0%-100% */
    uint256 internal tournamentEntranceFeeCut = 100;
    
    // Values 0-10,000 map to 0%-100% => 20%
    uint256 public tournamentOwnersCut;
    uint256 public tournamentIncentiveCut;
    
     /** @dev TournamentNewContender event. Emitted every time a warrior enters tournament
     *  @param owner Warrior owner
     *  @param warriorIds 5 Warrior IDs that entered tournament, packed into one uint256
     *  see CryptoUtils._packWarriorIds
     */
    event TournamentNewContender(address owner, uint256 warriorIds, uint256 entranceFee);
    
    /** @dev TournamentFinished event. Emitted every time a tournament is finished
     *  @param owners array of warrior group owners packed to uint256
     *  @param results number of wins for each group
     *  @param tournamentBank current tournament bank
     *  see CryptoUtils._packWarriorIds
     */
    event TournamentFinished(uint256[] owners, uint32[] results, uint256 tournamentBank);
    
    function Tournament(uint256 _pvpCut, uint256 _tournamentBankCut, 
    uint256 _pvpMaxIncentiveCut, uint256 _tournamentOwnersCut, uint256 _tournamentIncentiveCut) public
    PVP(_pvpCut, _tournamentBankCut, _pvpMaxIncentiveCut) 
    {
        require((_tournamentOwnersCut + _tournamentIncentiveCut) <= 10000);
		
		tournamentOwnersCut = _tournamentOwnersCut;
		tournamentIncentiveCut = _tournamentIncentiveCut;
    }
    
    
    
    // @dev Computes incentive reward for launching tournament finishTournament()
    // @param _tournamentBank
    function _computeTournamentIncentiveReward(uint256 _currentBank, uint256 _incentiveCut) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because _currentBank max is equal ~ 20000000 finney,
        // and (tournamentOwnersCut + tournamentIncentiveCut) <= 10000 (see the require()
        // statement in the Tournament constructor). The result of this
        // function is always guaranteed to be <= _currentBank.
        return _currentBank * _incentiveCut / 10000;
    }
    
    function _computeTournamentContenderCut(uint256 _incentiveCut) internal view returns (uint256) {
        // NOTE: (tournamentOwnersCut + tournamentIncentiveCut) <= 10000 (see the require()
        // statement in the Tournament constructor). The result of this
        // function is always guaranteed to be <= _reward.
        return 10000 - tournamentOwnersCut - _incentiveCut;
    }
    
    function _computeTournamentBeneficiaryFee(uint256 _currentBank) internal view returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because _currentBank max is equal ~ 20000000 finney,
        // and (tournamentOwnersCut + tournamentIncentiveCut) <= 10000 (see the require()
        // statement in the Tournament constructor). The result of this
        // function is always guaranteed to be <= _currentBank.
        return _currentBank * tournamentOwnersCut / 10000;
    }
    
    // @dev set tournament entrance fee cut, can be set only if
    // tournament queue is empty
    // @param _cut range from 0 - 10000, mapped to 0-100%
    function setTournamentEntranceFeeCut(uint256 _cut) external onlyOwner {
        //cut must be less or equal 100&
        require(_cut <= 10000);
        //tournament queue must be empty
        require(tournamentQueueSize == 0);
        //checks passed, set cut
		tournamentEntranceFeeCut = _cut;
    }
    
    function getTournamentEntranceFee() external view returns(uint256) {
        return currentTournamentBank * tournamentEntranceFeeCut / 10000;
    }
    
    //@dev returns tournament entrance fee - 3% threshold
    function getTournamentThresholdFee() public view returns(uint256) {
        return currentTournamentBank * tournamentEntranceFeeCut * (10000 - THRESHOLD) / 10000 / 10000;
    }
    
    //@dev returns max allowed tournament contenders, public because of internal use
    function maxTournamentContenders() public view returns(uint256){
        return tournamentQueue.length / DATA_SIZE;
    }
    
    function canFinishTournament() external view returns(bool) {
        return tournamentEndBlock <= block.number && tournamentQueueSize > 0;
    }
    
    // @dev Internal utility function to sigin up to tournament, 
    // assumes that all battle requirements have been checked.
    function _triggerNewTournamentContender(address _owner, uint256[] memory _tournamentData, uint256 _fee) internal {
        //pack warrior ids into uint256
        
        currentTournamentBank += _fee;
        
        uint256 packedWarriorIds = CryptoUtils._packWarriorIds(_tournamentData);
        //make composite warrior out of 5 warriors 
        uint256 combinedWarrior = CryptoUtils._combineWarriors(_tournamentData);
        
        //add to queue
        //icrement tournament queue
        uint256 size = tournamentQueueSize++ * DATA_SIZE;
        //record tournament data
		tournamentQueue[size++] = packedWarriorIds;
		tournamentQueue[size++] = combinedWarrior;
		warriorToOwner[CryptoUtils._unpackWarriorId(packedWarriorIds, 0)] = _owner;
		//
		//Emit event
		TournamentNewContender(_owner, packedWarriorIds, _fee);
    }
    
    function addTournamentContender(address _owner, uint256[] _tournamentData) external payable TournamentNotPaused{
        // Caller must be pvpListener contract
        require(msg.sender == address(pvpListener));
        
        require(_owner != address(0));
        //
        //check current tournament bank > 0
        require(pvpBattleFee == 0 || currentTournamentBank > 0);
        //
        //check that there is enough funds to pay entrance fee
        uint256 fee = getTournamentThresholdFee();
        require(msg.value >= fee);
        //owner must withdraw all booty before contending pvp
        require(ownerToBooty[_owner] == 0);
        //
        //check that warriors group is exactly of allowed size
        require(_tournamentData.length == GROUP_SIZE);
        //
        //check that there is enough room for new participants
        require(tournamentQueueSize < maxTournamentContenders());
        //
        //check that admission started
        require(block.number >= getTournamentAdmissionBlock());
        //check that admission not ended
        require(block.number <= tournamentEndBlock);
        
        //all checks passed, trigger sign up
        _triggerNewTournamentContender(_owner, _tournamentData, fee);
    }
    
    //@dev collect all combined warriors data
    function getCombinedWarriors() internal view returns(uint256[] memory warriorsData) {
        uint256 length = tournamentQueueSize;
        warriorsData = new uint256[](length);
        
        for(uint256 i = 0; i < length; i ++) {
            // Grab the combined warrior data in storage.
            warriorsData[i] = tournamentQueue[i * DATA_SIZE + 1];
        }
        return warriorsData;
    }
    
    function getTournamentState() external view returns
    (uint256 contendersCount, uint256 bank, uint256 admissionStartBlock, uint256 endBlock, uint256 incentiveReward)
    {
    	contendersCount = tournamentQueueSize;
    	bank = currentTournamentBank;
    	admissionStartBlock = getTournamentAdmissionBlock();   
    	endBlock = tournamentEndBlock;
    	incentiveReward = _computeTournamentIncentiveReward(bank, _computeIncentiveCut(bank, tournamentIncentiveCut));
    }
    
    function _repackToCombinedIds(uint256[] memory _warriorsData) internal view {
        uint256 length = _warriorsData.length;
        for(uint256 i = 0; i < length; i ++) {
            _warriorsData[i] = tournamentQueue[i * DATA_SIZE];
        }
    }
    
    // @dev Computes warrior pvp reward
    // @param _totalBet - total bet from both competitors.
    function _computeTournamentBooty(uint256 _currentBank, uint256 _contenderResult, uint256 _totalBattles) internal pure returns (uint256){
        // NOTE: We don't use SafeMath (or similar) in this function because _currentBank max is equal ~ 20000000 finney,
        // _totalBattles is guaranteed to be > 0 and <= 400, and (tournamentOwnersCut + tournamentIncentiveCut) <= 10000 (see the require()
        // statement in the Tournament constructor). The result of this
        // function is always guaranteed to be <= _reward.
        // return _currentBank * (10000 - tournamentOwnersCut - _incentiveCut) * _result / 10000 / _totalBattles;
        return _currentBank * _contenderResult / _totalBattles;
        
    }
    
    function _grandTournamentBooty(uint256 _warriorIds, uint256 _currentBank, uint256 _contenderResult, uint256 _totalBattles)
    internal returns (uint256)
    {
        uint256 warriorId = CryptoUtils._unpackWarriorId(_warriorIds, 0);
        address owner = warriorToOwner[warriorId];
        uint256 booty = _computeTournamentBooty(_currentBank, _contenderResult, _totalBattles);
        return sendBooty(owner, booty);
    }
    
    function _grandTournamentRewards(uint256 _currentBank, uint256[] memory _warriorsData, uint32[] memory _results) internal returns (uint256){
        uint256 length = _warriorsData.length;
        uint256 totalBattles = CryptoUtils._getTournamentBattles(length) * 10000;//*10000 required for booty computation
        uint256 incentiveCut = _computeIncentiveCut(_currentBank, tournamentIncentiveCut);
        uint256 contenderCut = _computeTournamentContenderCut(incentiveCut);
        
        uint256 failedBooty = 0;
        for(uint256 i = 0; i < length; i ++) {
            //grand rewards
            failedBooty += _grandTournamentBooty(_warriorsData[i], _currentBank, _results[i] * contenderCut, totalBattles);
        }
        //send beneficiary fee
        failedBooty += sendBooty(pvpListener.getBeneficiary(), _computeTournamentBeneficiaryFee(_currentBank));
        if (failedBooty > 0) {
            totalBooty += failedBooty;
        }
        return _computeTournamentIncentiveReward(_currentBank, incentiveCut);
    }
    
    function _repackToWarriorOwners(uint256[] memory warriorsData) internal view {
        uint256 length = warriorsData.length;
        for (uint256 i = 0; i < length; i ++) {
            warriorsData[i] = uint256(warriorToOwner[CryptoUtils._unpackWarriorId(warriorsData[i], 0)]);
        }
    }
    
    function _triggerFinishTournament() internal returns(uint256){
        //hold 10 random battles for each composite warrior
        uint256[] memory warriorsData = getCombinedWarriors();
        uint32[] memory results = CryptoUtils.getTournamentBattleResults(warriorsData, tournamentEndBlock - 1);
        //repack combined warriors id
        _repackToCombinedIds(warriorsData);
        //notify pvp listener
        pvpListener.tournamentFinished(warriorsData);
        //reschedule
        //clear tournament
        tournamentQueueSize = 0;
        //schedule new tournament
        _scheduleTournament();
        
        uint256 currentBank = currentTournamentBank;
        currentTournamentBank = 0;//nullify before sending to users
        //grand rewards, not susceptible to reentry attack
        //because of require(tournamentEndBlock <= block.number)
        //and require(tournamentQueueSize > 0) and currentTournamentBank == 0
        uint256 incentiveReward = _grandTournamentRewards(currentBank, warriorsData, results);
        
        currentTournamentBank = nextTournamentBank;
        nextTournamentBank = 0;
        
        _repackToWarriorOwners(warriorsData);
        
        //emit event
        TournamentFinished(warriorsData, results, currentBank);

        return incentiveReward;
    }
    
    function finishTournament() external TournamentNotPaused {
        //make all the checks
        // tournament is ready to be executed
        require(tournamentEndBlock <= block.number);
        // we have participants
        require(tournamentQueueSize > 0);
        
        uint256 incentiveReward = _triggerFinishTournament();
        
        //give reward for incentive
        safeSend(msg.sender, incentiveReward);
    }
    
    
    // @dev Remove all PVP contenders from PVP queue 
    //  and return all entrance fees to warrior owners.
    //  NB: this is emergency method, used only in f%#^@up situation
    function removeAllTournamentContenders() external onlyOwner TournamentPaused {
        //remove all pvp contenders
        uint256 length = tournamentQueueSize;
        
        uint256 warriorId;
        uint256 failedBooty;
        uint256 i;

        uint256 fee;
        uint256 bank = currentTournamentBank;
        
        uint256[] memory warriorsData = new uint256[](length);
        //get tournament warriors
        for(i = 0; i < length; i ++) {
            warriorsData[i] = tournamentQueue[i * DATA_SIZE];
        }
        //notify pvp listener
        pvpListener.tournamentFinished(warriorsData);
        //return entrance fee to warrior owners
     	currentTournamentBank = 0;
        tournamentQueueSize = 0;

        for(i = length - 1; i >= 0; i --) {
            //return entrance fee
            warriorId = CryptoUtils._unpackWarriorId(warriorsData[i], 0);
            //compute contender entrance fee
			fee = bank - (bank * 10000 / (tournamentEntranceFeeCut * (10000 - THRESHOLD) / 10000 + 10000));
			//return entrance fee to owner
	        failedBooty += sendBooty(warriorToOwner[warriorId], fee);
	        //subtract fee from bank, for next use
	        bank -= fee;
        }
        currentTournamentBank = bank;
        totalBooty += failedBooty;
    }
}

contract BattleProvider is Tournament {
    
    function BattleProvider(address _pvpListener, uint256 _pvpCut, uint256 _tournamentCut, uint256 _incentiveCut, 
    uint256 _tournamentOwnersCut, uint256 _tournamentIncentiveCut) public 
    Tournament(_pvpCut, _tournamentCut, _incentiveCut, _tournamentOwnersCut, _tournamentIncentiveCut) 
    {
        PVPListenerInterface candidateContract = PVPListenerInterface(_pvpListener);
        // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
        require(candidateContract.isPVPListener());
        // Set the new contract address
        pvpListener = candidateContract;
        
        // the creator of the contract is the initial owner
        owner = msg.sender;
    }
    
    
    // @dev Sanity check that allows us to ensure that we are pointing to the
    // right BattleProvider in our setBattleProviderAddress() call.
    function isPVPProvider() external pure returns (bool) {
        return true;
    }
    
    function setSecondsPerBlock(uint256 secs) external onlyOwner {
        secondsPerBlock = secs;
    }
}


/* warrior identity generator*/
contract WarriorGenerator is Pausable, SanctuaryInterface {
    
    CryptoWarriorCore public coreContract;
    
    /* LIMITS */
    uint32[19] public parameters;/*  = [
        uint32(10),//0_bodyColorMax3
        uint32(10),//1_eyeshMax4
        uint32(10),//2_mouthMax5
        uint32(20),//3_heirMax6
        uint32(10),//4_heirColorMax7
        uint32(3),//5_armorMax8
        uint32(3),//6_weaponMax9
        uint32(3),//7_hatMax10
        uint32(4),//8_runesMax11
        uint32(1),//9_wingsMax12
        uint32(10),//10_petMax13
        uint32(6),//11_borderMax14
        uint32(6),//12_backgroundMax15
        uint32(10),//13_unique
        uint32(900),//14_legendary
        uint32(9000),//15_mythic
        uint32(90000),//16_rare
        uint32(900000),//17_uncommon
        uint32(0)//18_uniqueTotal
    ];*/
    

    function changeParameter(uint32 _paramIndex, uint32 _value) external onlyOwner {
        CryptoUtils._changeParameter(_paramIndex, _value, parameters);
    }

    // / @dev simply a boolean to indicate this is the contract we expect to be
    function isSanctuary() public pure returns (bool){
        return true;
    }

    // / @dev generate new warrior identity
    // / @param _heroIdentity Genes of warrior that invoked resurrection, if 0 => Demigod gene that signals to generate unique warrior
    // / @param _heroLevel Level of the warrior
    // / @_targetBlock block number from which hash will be taken
    // / @_perkId special perk id, like MINER(1)
    // / @return the identity that are supposed to be passed down to newly arisen warrior
    function generateWarrior(uint256 _heroIdentity, uint256 _heroLevel, uint256 _targetBlock, uint256 _perkId) 
    public returns (uint256) 
    {
        //only core contract can call this method
        require(msg.sender == address(coreContract));
        
        return _generateIdentity(_heroIdentity, _heroLevel, _targetBlock, _perkId);
    }
    
    function _generateIdentity(uint256 _heroIdentity, uint256 _heroLevel, uint256 _targetBlock, uint256 _perkId) internal returns(uint256){
        
        //get memory copy, to reduce storage read requests
        uint32[19] memory memoryParams = parameters;
        //generate warrior identity
        uint256 identity = CryptoUtils.generateWarrior(_heroIdentity, _heroLevel, _targetBlock, _perkId, memoryParams);
        
        //validate before pushing changes to storage
        CryptoUtils._validateIdentity(identity, memoryParams);
        //push changes to storage
        CryptoUtils._recordWarriorData(identity, parameters);
        
        return identity;
    }
}

contract WarriorSanctuary is WarriorGenerator {
    uint256 internal constant SUMMONING_SICKENESS = 12 hours;
    uint256 internal constant RITUAL_DURATION = 15 minutes;
    /// @notice The payment required to use startRitual().
    uint256 public ritualFee = 10 finney;
    
    uint256 public constant RITUAL_COMPENSATION = 2 finney;
    
    mapping(address => uint256) public soulCounter;
    //
    mapping(address => uint256) public ritualTimeBlock;
    
    bool public recoveryAllowed = true;
    
    event WarriorBurned(uint256 warriorId, address owner);
    event RitualStarted(address owner, uint256 numberOfSouls);
    event RitualFinished(address owner, uint256 numberOfSouls, uint256 newWarriorId);
    
    
    function WarriorSanctuary(address _coreContract, uint32[] _settings) public {
        uint256 length = _settings.length;
        require(length == 18);
        require(_settings[8] == 4);//check runes max
        require(_settings[10] == 10);//check pets max
        require(_settings[11] == 5);//check border max
        require(_settings[12] == 6);//check background max
        //setup parameters
        for(uint256 i = 0; i < length; i ++) {
            parameters[i] = _settings[i];
        }	
        
        //set core
        CryptoWarriorCore coreCondidat = CryptoWarriorCore(_coreContract);
        require(coreCondidat.isPVPListener());
        coreContract = coreCondidat;
        
    }
    
    function recoverSouls(address[] owners, uint256[] souls, uint256[] blocks) external onlyOwner {
        require(recoveryAllowed);
        
        uint256 length = owners.length;
        require(length == souls.length && length == blocks.length);
        
        for(uint256 i = 0; i < length; i ++) {
            soulCounter[owners[i]] = souls[i];
            ritualTimeBlock[owners[i]] = blocks[i];
        }
        
        recoveryAllowed = false;
    }
    
    
    //burn warrior
    function burnWarrior(uint256 _warriorId) whenNotPaused external {
        coreContract.burnWarrior(_warriorId, msg.sender);
        
        soulCounter[msg.sender] ++;
        
        WarriorBurned(_warriorId, msg.sender);
    }
   
    
    function startRitual() whenNotPaused external payable {
        // Checks for payment.
        require(msg.value >= ritualFee);
        
        uint256 souls = soulCounter[msg.sender];
        // Check that address has at least 10 burned souls
        require(souls >= 10);
        //
        //Check that no rituals are in progress
        require(ritualTimeBlock[msg.sender] == 0);
        
        ritualTimeBlock[msg.sender] = RITUAL_DURATION / coreContract.secondsPerBlock() + block.number;
        
        // Calculate any excess funds included in msg.value. If the excess
        // is anything worth worrying about, transfer it back to message owner.
        // NOTE: We checked above that the msg.value is greater than or
        // equal to the price so this cannot underflow.
        uint256 feeExcess = msg.value - ritualFee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of _isReadyToPVE check
        // will fail
        if (feeExcess > 0) {
            msg.sender.transfer(feeExcess);
        }
        //send battle fee to beneficiary
        coreContract.getBeneficiary().transfer(ritualFee - RITUAL_COMPENSATION);
        
        RitualStarted(msg.sender, souls);
    }
    
    
    //arise warrior
    function finishRitual(address _owner) whenNotPaused external {
        // Check ritual time is over
        uint256 timeBlock = ritualTimeBlock[_owner];
        require(timeBlock > 0 && timeBlock <= block.number);
        
        uint256 souls = soulCounter[_owner];
        
        require(souls >= 10);
        
        uint256 identity = _generateIdentity(uint256(_owner), souls, timeBlock - 1, 0);
        
        uint256 warriorId = coreContract.ariseWarrior(identity, _owner, block.number + (SUMMONING_SICKENESS / coreContract.secondsPerBlock()));
    
        soulCounter[_owner] = 0;
        ritualTimeBlock[_owner] = 0;
        //send compensation
        msg.sender.transfer(RITUAL_COMPENSATION);
        
        RitualFinished(_owner, 10, warriorId);
    }
    
    function setRitualFee(uint256 _pveRitualFee) external onlyOwner {
        require(_pveRitualFee > RITUAL_COMPENSATION);
        ritualFee = _pveRitualFee;
    }
}

contract AuctionBase {
	uint256 public constant PRICE_CHANGE_TIME_STEP = 15 minutes;
    //
    struct Auction{
        address seller;
        uint128 startingPrice;
        uint128 endingPrice;
        uint64 duration;
        uint64 startedAt;
    }
    mapping (uint256 => Auction) internal tokenIdToAuction;
    uint256 public ownerCut;
    ERC721 public nonFungibleContract;

    event AuctionCreated(uint256 tokenId, address seller, uint256 startingPrice);

    event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner, address seller);

    event AuctionCancelled(uint256 tokenId, address seller);

    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool){
        return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
    }

    function _escrow(address _owner, uint256 _tokenId) internal{
        nonFungibleContract.transferFrom(_owner, address(this), _tokenId);
    }

    function _transfer(address _receiver, uint256 _tokenId) internal{
        nonFungibleContract.transfer(_receiver, _tokenId);
    }

    function _addAuction(uint256 _tokenId, Auction _auction) internal{
        require(_auction.duration >= 1 minutes);
        
        tokenIdToAuction[_tokenId] = _auction;
        
        AuctionCreated(uint256(_tokenId), _auction.seller, _auction.startingPrice);
    }

    function _cancelAuction(uint256 _tokenId, address _seller) internal{
        _removeAuction(_tokenId);
        
        _transfer(_seller, _tokenId);
        
        AuctionCancelled(_tokenId, _seller);
    }

    function _bid(uint256 _tokenId, uint256 _bidAmount) internal returns (uint256){
        
        Auction storage auction = tokenIdToAuction[_tokenId];
        
        require(_isOnAuction(auction));
        
        uint256 price = _currentPrice(auction);
        
        require(_bidAmount >= price);
        
        address seller = auction.seller;
        
        _removeAuction(_tokenId);
        
        if (price > 0) {
            uint256 auctioneerCut = _computeCut(price);
            uint256 sellerProceeds = price - auctioneerCut;
            seller.transfer(sellerProceeds);
            nonFungibleContract.getBeneficiary().transfer(auctioneerCut);
        }
        
        uint256 bidExcess = _bidAmount - price;
        
        msg.sender.transfer(bidExcess);
        
        AuctionSuccessful(_tokenId, price, msg.sender, seller);
        
        return price;
    }

    function _removeAuction(uint256 _tokenId) internal{
        delete tokenIdToAuction[_tokenId];
    }

    function _isOnAuction(Auction storage _auction) internal view returns (bool){
        return (_auction.startedAt > 0);
    }

    function _currentPrice(Auction storage _auction)
        internal
        view
        returns (uint256){
        uint256 secondsPassed = 0;
        
        if (now > _auction.startedAt) {
            secondsPassed = now - _auction.startedAt;
        }
        
        return _computeCurrentPrice(_auction.startingPrice,
            _auction.endingPrice,
            _auction.duration,
            secondsPassed);
    }
    
    function _computeCurrentPrice(uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration,
        uint256 _secondsPassed)
        internal
        pure
        returns (uint256){
        if (_secondsPassed >= _duration) {
            return _endingPrice;
        } else {
            int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
            
            int256 currentPriceChange = totalPriceChange * int256(_secondsPassed / PRICE_CHANGE_TIME_STEP * PRICE_CHANGE_TIME_STEP) / int256(_duration);
            
            int256 currentPrice = int256(_startingPrice) + currentPriceChange;
            
            return uint256(currentPrice);
        }
    }

    function _computeCut(uint256 _price) internal view returns (uint256){
        
        return _price * ownerCut / 10000;
    }
}

contract SaleClockAuction is Pausable, AuctionBase {
    
    bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9f40b779);
    
    bool public isSaleClockAuction = true;
    uint256 public minerSaleCount;
    uint256[5] public lastMinerSalePrices;

    function SaleClockAuction(address _nftAddress, uint256 _cut) public{
        require(_cut <= 10000);
        ownerCut = _cut;
        ERC721 candidateContract = ERC721(_nftAddress);
        require(candidateContract.supportsInterface(InterfaceSignature_ERC721));
        require(candidateContract.getBeneficiary() != address(0));
        
        nonFungibleContract = candidateContract;
    }

    function cancelAuction(uint256 _tokenId)
        external{
        
        AuctionBase.Auction storage auction = tokenIdToAuction[_tokenId];
        
        require(_isOnAuction(auction));
        
        address seller = auction.seller;
        
        require(msg.sender == seller);
        
        _cancelAuction(_tokenId, seller);
    }

    function cancelAuctionWhenPaused(uint256 _tokenId)
        whenPaused
        onlyOwner
        external{
        AuctionBase.Auction storage auction = tokenIdToAuction[_tokenId];
        require(_isOnAuction(auction));
        _cancelAuction(_tokenId, auction.seller);
    }

    function getCurrentPrice(uint256 _tokenId)
        external
        view
        returns (uint256){
        
        AuctionBase.Auction storage auction = tokenIdToAuction[_tokenId];
        
        require(_isOnAuction(auction));
        
        return _currentPrice(auction);
    }
    
    function createAuction(uint256 _tokenId,
        uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration,
        address _seller)
        whenNotPaused
        external{
        require(_startingPrice == uint256(uint128(_startingPrice)));
        require(_endingPrice == uint256(uint128(_endingPrice)));
        require(_duration == uint256(uint64(_duration)));
        require(msg.sender == address(nonFungibleContract));
        _escrow(_seller, _tokenId);
        
        AuctionBase.Auction memory auction = Auction(_seller,
            uint128(_startingPrice),
            uint128(_endingPrice),
            uint64(_duration),
            uint64(now));
        
        _addAuction(_tokenId, auction);
    }
    
    function bid(uint256 _tokenId)
        whenNotPaused
        external
        payable{
        
        address seller = tokenIdToAuction[_tokenId].seller;
        
        uint256 price = _bid(_tokenId, msg.value);
        
        _transfer(msg.sender, _tokenId);
        
        if (seller == nonFungibleContract.getBeneficiary()) {
            lastMinerSalePrices[minerSaleCount % 5] = price;
            minerSaleCount++;
        }
    }

    function averageMinerSalePrice() external view returns (uint256){
        uint256 sum = 0;
        for (uint256 i = 0; i < 5; i++){
            sum += lastMinerSalePrices[i];
        }
        return sum / 5;
    }
    
    /**getAuctionsById returns packed actions data
     * @param tokenIds ids of tokens, whose auction's must be active 
     * @return auctionData as uint256 array
     * @return stepSize number of fields describing auction 
     */
    function getAuctionsById(uint32[] tokenIds) external view returns(uint256[] memory auctionData, uint32 stepSize) {
        stepSize = 6;
        auctionData = new uint256[](tokenIds.length * stepSize);
        
        uint32 tokenId;
        for(uint32 i = 0; i < tokenIds.length; i ++) {
            tokenId = tokenIds[i];
            AuctionBase.Auction storage auction = tokenIdToAuction[tokenId];
            require(_isOnAuction(auction));
            _setTokenData(auctionData, auction, tokenId, i * stepSize);
        }
    }
    
    /**getAuctions returns packed actions data
     * @param fromIndex warrior index from global warrior storage (aka warriorId)
     * @param count Number of auction's to find, if count == 0, then exact warriorId(fromIndex) will be searched
     * @return auctionData as uint256 array
     * @return stepSize number of fields describing auction 
     */
    function getAuctions(uint32 fromIndex, uint32 count) external view returns(uint256[] memory auctionData, uint32 stepSize) {
        stepSize = 6;
        if (count == 0) {
            AuctionBase.Auction storage auction = tokenIdToAuction[fromIndex];
	        	require(_isOnAuction(auction));
	        	auctionData = new uint256[](1 * stepSize);
	        	_setTokenData(auctionData, auction, fromIndex, count);
	        	return (auctionData, stepSize);
        } else {
            uint256 totalWarriors = nonFungibleContract.totalSupply();
	        if (totalWarriors == 0) {
	            // Return an empty array
	            return (new uint256[](0), stepSize);
	        } else {
	
	            uint32 totalSize = 0;
	            uint32 tokenId;
	            uint32 size = 0;
				auctionData = new uint256[](count * stepSize);
	            for (tokenId = 0; tokenId < totalWarriors && size < count; tokenId++) {
	                AuctionBase.Auction storage auction1 = tokenIdToAuction[tokenId];
	        
		        		if (_isOnAuction(auction1)) {
		        		    totalSize ++;
		        		    if (totalSize > fromIndex) {
		        		        _setTokenData(auctionData, auction1, tokenId, size++ * stepSize);//warriorId;
		        		    }
		        		}
	            }
	            
	            if (size < count) {
	                size *= stepSize;
	                uint256[] memory repack = new uint256[](size);
	                for(tokenId = 0; tokenId < size; tokenId++) {
	                    repack[tokenId] = auctionData[tokenId];
	                }
	                return (repack, stepSize);
	            }
	
	            return (auctionData, stepSize);
	        }
        }
    }
    
    // @dev Returns auction info for an NFT on auction.
    // @param _tokenId - ID of NFT on auction.
    function getAuction(uint256 _tokenId) external view returns(
        address seller,
        uint256 startingPrice,
        uint256 endingPrice,
        uint256 duration,
        uint256 startedAt
        ){
        
        Auction storage auction = tokenIdToAuction[_tokenId];
        
        require(_isOnAuction(auction));
        
        return (auction.seller,
            auction.startingPrice,
            auction.endingPrice,
            auction.duration,
            auction.startedAt);
    }
    
    //pack NFT data into specified array
    function _setTokenData(uint256[] memory auctionData, 
        AuctionBase.Auction storage auction, uint32 tokenId, uint32 index
    ) internal view {
        auctionData[index] = uint256(tokenId);//0
        auctionData[index + 1] = uint256(auction.seller);//1
        auctionData[index + 2] = uint256(auction.startingPrice);//2
        auctionData[index + 3] = uint256(auction.endingPrice);//3
        auctionData[index + 4] = uint256(auction.duration);//4
        auctionData[index + 5] = uint256(auction.startedAt);//5
    }
    
}

Contract Security Audit

Contract ABI

[{"constant":false,"inputs":[],"name":"removeAllPVPContenders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pvpMaxIncentiveCut","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"grabBooty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tournamentQueue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PVP_INTERVAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tournamentQueueSize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tournamentBankCut","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ADMISSION_TIME","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpausePVP","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getTournamentThresholdFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"RATING_EXPAND_INTERVAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tournamentEndBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pvpOwnerCut","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"canFinishTournament","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"pvpQueue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"canFinishPVP","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextTournamentBank","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishPVP","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getTournamentAdmissionBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"secs","type":"uint256"}],"name":"setSecondsPerBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_cut","type":"uint256"}],"name":"setTournamentEntranceFeeCut","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"uint256"}],"name":"setPVPEntranceFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPVPState","outputs":[{"name":"contendersCount","type":"uint256"},{"name":"matchingCount","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"incentiveReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_levelPoints","type":"uint256"}],"name":"getPVPEntranceFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishTournament","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"removeAllTournamentContenders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getTournamentState","outputs":[{"name":"contendersCount","type":"uint256"},{"name":"bank","type":"uint256"},{"name":"admissionStartBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"incentiveReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FUND_GATHERING_TIME","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pvpPaused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pausePVP","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTournamentEntranceFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentTournamentBank","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tournamentIncentiveCut","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextPVPBatleBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_warriorId","type":"uint256"}],"name":"removePVPContender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_packedWarrior","type":"uint256"}],"name":"addPVPContender","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"totalBooty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tournamentOwnersCut","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"ownerToBooty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxTournamentContenders","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpauseTournament","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tournamentPaused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pauseTournament","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"maxPVPContenders","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pvpQueueSize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPVPProvider","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_tournamentData","type":"uint256[]"}],"name":"addTournamentContender","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"warriorIds","type":"uint32[]"}],"name":"getPVPCycles","outputs":[{"name":"","type":"uint32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_pvpListener","type":"address"},{"name":"_pvpCut","type":"uint256"},{"name":"_tournamentCut","type":"uint256"},{"name":"_incentiveCut","type":"uint256"},{"name":"_tournamentOwnersCut","type":"uint256"},{"name":"_tournamentIncentiveCut","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"warriorIds","type":"uint256"},{"indexed":false,"name":"entranceFee","type":"uint256"}],"name":"TournamentNewContender","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owners","type":"uint256[]"},{"indexed":false,"name":"results","type":"uint32[]"},{"indexed":false,"name":"tournamentBank","type":"uint256"}],"name":"TournamentFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tournamentEndBlock","type":"uint256"}],"name":"TournamentScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nextPVPBatleBlock","type":"uint256"}],"name":"PVPScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"warriorId","type":"uint256"},{"indexed":false,"name":"entranceFee","type":"uint256"}],"name":"PVPNewContender","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"warriorsData","type":"uint256[]"},{"indexed":false,"name":"owners","type":"address[]"},{"indexed":false,"name":"matchingCount","type":"uint256"}],"name":"PVPFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"BootySendFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"receiver","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"BootyGrabbed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"warriorId","type":"uint256"},{"indexed":false,"name":"owner","type":"address"}],"name":"PVPContenderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"paused","type":"bool"}],"name":"PausePVP","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"paused","type":"bool"}],"name":"PauseTournament","type":"event"}]

60606040526000805460a060020a61ffff02191681556065819055600f606855662386f26fc10000606b55606c819055606d819055606e819055607181905560725560646101145534156200005357600080fd5b60405160c08062003add83398101604052808051919060200180519190602001805191906020018051919060200180519190602001805160008054600160a060020a03191633600160a060020a03161781559092509050858585858584848461271082840182011115620000c657600080fd5b606992909255606f55606a556127108282011115620000e457600080fd5b61011591909155610116555088925050600160a060020a038216905063e4d9d2126000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156200015657600080fd5b6102c65a03f115156200016857600080fd5b5050506040518051905015156200017e57600080fd5b60738054600160a060020a03928316600160a060020a03199182161790915560008054339093169290911691909117905550505050505061391880620001c56000396000f3006060604052600436106102425763ffffffff60e060020a600035041663059d45da81146102475780630c26a8161461025c5780630ca435a61461028157806315834aed146102945780631924f74a146102aa5780631ba5e63f146102bd5780631c44bac7146102d05780631ca43cef146102e357806320154e78146102f657806324abfc02146103095780632a6c703b1461031c5780634474809f1461032f5780634619aa191461034257806346682cab14610355578063477e5d241461037c5780634a7761041461039257806351c85565146103a5578063534e3f81146103b857806354070f83146103cb5780635663896e146103de57806359bb7867146103f45780635c7d64251461040a5780636147bcb914610420578063637492251461045e5780636b2bb92a146104745780636f6f828e1461048757806374af3ee11461049a5780637a314ce2146104df5780637ced55c2146104f257806389cda6a8146105055780638da5cb5b146105185780638db7cd54146105475780639d619dad1461055a578063a43b92a21461056d578063a48255b714610580578063a72f3dea14610593578063a99306e7146105a9578063af10b8cf146105c0578063b3a810f7146105d3578063bd1954c6146105e6578063c6169e1f14610605578063d08279b014610618578063d34468561461062b578063d8a489d91461063e578063f15a9ae814610651578063f2fde38b14610664578063f73c8dfd14610683578063f773ab8014610696578063f8b608a1146106a9578063fe1b602a146106c9575b600080fd5b341561025257600080fd5b61025a61073a565b005b341561026757600080fd5b61026f61085f565b60405190815260200160405180910390f35b341561028c57600080fd5b61025a610865565b341561029f57600080fd5b61026f60043561092e565b34156102b557600080fd5b61026f610942565b34156102c857600080fd5b61026f610948565b34156102db57600080fd5b61026f61094e565b34156102ee57600080fd5b61026f610954565b341561030157600080fd5b61025a61095a565b341561031457600080fd5b61026f6109e2565b341561032757600080fd5b61026f6109ff565b341561033a57600080fd5b61026f610a05565b341561034d57600080fd5b61026f610a0b565b341561036057600080fd5b610368610a11565b604051901515815260200160405180910390f35b341561038757600080fd5b61026f600435610a2c565b341561039d57600080fd5b610368610a39565b34156103b057600080fd5b61026f610a6e565b34156103c357600080fd5b61025a610a74565b34156103d657600080fd5b61026f610ae9565b34156103e957600080fd5b61025a600435610b1a565b34156103ff57600080fd5b61025a600435610b3a565b341561041557600080fd5b61025a600435610b77565b341561042b57600080fd5b610433610ba4565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b341561046957600080fd5b61026f600435610bea565b341561047f57600080fd5b61025a610bff565b341561049257600080fd5b61025a610c4e565b34156104a557600080fd5b6104ad610e46565b604051808681526020018581526020018481526020018381526020018281526020019550505050505060405180910390f35b34156104ea57600080fd5b61026f610e7e565b34156104fd57600080fd5b610368610e85565b341561051057600080fd5b61025a610e95565b341561052357600080fd5b61052b610f23565b604051600160a060020a03909116815260200160405180910390f35b341561055257600080fd5b61026f610f32565b341561056557600080fd5b61026f610f46565b341561057857600080fd5b61026f610f4c565b341561058b57600080fd5b61026f610f53565b341561059e57600080fd5b61025a600435610f59565b61025a600160a060020a0360043516602435611105565b34156105cb57600080fd5b61026f6111d1565b34156105de57600080fd5b61026f6111d7565b34156105f157600080fd5b61026f600160a060020a03600435166111de565b341561061057600080fd5b61026f6111f0565b341561062357600080fd5b61025a6111fb565b341561063657600080fd5b610368611284565b341561064957600080fd5b61025a611294565b341561065c57600080fd5b61026f611323565b341561066f57600080fd5b61025a600160a060020a0360043516611328565b341561068e57600080fd5b61026f61137f565b34156106a157600080fd5b610368611385565b61025a60048035600160a060020a0316906024803590810191013561138a565b34156106d457600080fd5b6106e760048035602481019101356114ad565b60405160208082528190810183818151815260200191508051906020019060200280838360005b8381101561072657808201518382015260200161070e565b505050509050019250505060405180910390f35b600080548190819081908190819033600160a060020a0390811691161461076057600080fd5b60005460a060020a900460ff16151561077857600080fd5b506065805460009182905595505b8581101561084f576001816064811061079b57fe5b015494506107a88561158b565b607354909450600160a060020a031663d7d3c4d5856040518263ffffffff1660e060020a028152600401808263ffffffff168152602001915050600060405180830381600087803b15156107fb57600080fd5b6102c65a03f1151561080c57600080fd5b505050600084815260676020526040902054600160a060020a031691506108438261083e610839886115b8565b6115c9565b6115d0565b90920191600101610786565b5050606d80549091019055505050565b606a5481565b600160a060020a03331660009081526066602052604081205490811161088a57600080fd5b606d548190101561089a57600080fd5b600160a060020a03331660008181526066602052604080822091909155606d8054849003905582156108fc0290839051600060405180830381858888f1935050505015156108e757600080fd5b7fd7a49720c00abce919ff2e9064ad8b7c58f0f539d778c4442b4c86422462d11b3382604051600160a060020a03909216825260208201526040908101905180910390a150565b60748160a0811061093b57fe5b0154905081565b61038481565b606e5481565b606f5481565b61a8c081565b60005433600160a060020a0390811691161461097557600080fd5b60005460a060020a900460ff16151561098d57600080fd5b6000805474ff0000000000000000000000000000000000000000191681557f4e494720a65633d819abbff3aa9af070f5451ce37f40dcdbb5b78b6bbbd8eac490604051901515815260200160405180910390a1565b6101145460715460009161271091026125e4028190045b04905090565b610e1081565b60705481565b60695481565b60004360705411158015610a2757506000606e54115b905090565b6001816064811061093b57fe5b600043606c5411158015610a2757506001610a68610a55611687565b610384610a606116f4565b610e1061171a565b11905090565b60725481565b610a7c6138da565b60008054819060a060020a900460ff1615610a9657600080fd5b606c5443901115610aa657600080fd5b610aae611687565b9250610abf83610384610a606116f4565b915060018211610ace57600080fd5b610ad8838361184e565b9050610ae43382611a3d565b505050565b60008060685461a8c0811515610afb57fe5b0490508060705410610b11578060705403610b14565b60005b91505090565b60005433600160a060020a03908116911614610b3557600080fd5b606855565b60005433600160a060020a03908116911614610b5557600080fd5b612710811115610b6457600080fd5b606e5415610b7157600080fd5b61011455565b60005433600160a060020a03908116911614610b9257600080fd5b60655415610b9f57600080fd5b606b55565b600080600080610bb26138da565b610bba611687565b90506065549450610bd081610384610a606116f4565b9350606c549250610be18185611a61565b91505090919293565b6000610bf582611a88565b606b540292915050565b6000805460a860020a900460ff1615610c1757600080fd5b60705443901115610c2757600080fd5b606e5460009011610c3757600080fd5b610c3f611a92565b9050610c4b3382611a3d565b50565b600080600080600080610c5f6138da565b60005433600160a060020a03908116911614610c7a57600080fd5b60005460a860020a900460ff161515610c9257600080fd5b606e549650607154915086604051805910610caa5750595b90808252806020026020018201604052509050600093505b86841015610cfd5760746002850260a08110610cda57fe5b0154818581518110610ce857fe5b60209081029091010152600190930192610cc2565b607354600160a060020a03166311196cc2826040518263ffffffff1660e060020a0281526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d61578082015183820152602001610d49565b5050505090500192505050600060405180830381600087803b1515610d8557600080fd5b6102c65a03f11515610d9657600080fd5b505060006071819055606e5550600019870193505b60008410610e3357610dd3818581518110610dc257fe5b906020019060200201516000611c6b565b61011454909650612710906125e40204612710018261271002811515610df557fe5b60008881526067602052604090205491900483039350610e1e90600160a060020a0316846115d0565b90940193600019909301929082900390610dab565b506071555050606d805490910190555050565b606e5460715460008080610e58610ae9565b92506070549150610e7584610e708661011654611c9c565b611ce9565b90509091929394565b6201518081565b60005460a060020a900460ff1681565b60005433600160a060020a03908116911614610eb057600080fd5b60005460a060020a900460ff1615610ec757600080fd5b6000805474ff0000000000000000000000000000000000000000191660a060020a1790557f4e494720a65633d819abbff3aa9af070f5451ce37f40dcdbb5b78b6bbbd8eac46001604051901515815260200160405180910390a1565b600054600160a060020a031681565b6101145460715460009161271091026109f9565b60715481565b6101165481565b606c5481565b606554600080808311610f6b57600080fd5b60008481526067602052604090205433600160a060020a03908116911614610f9257600080fd5b610f9b84611cf6565b91506000821215610fab57600080fd5b600163ffffffff831660648110610fbe57fe5b015490506004610fcc6116f4565b610fd583611d41565b011015610fe157600080fd5b6001830382101561101857606554600190600019016064811061100057fe5b0154600163ffffffff84166064811061101557fe5b01555b60658054600019019055607354600160a060020a031663d7d3c4d58560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561106a57600080fd5b6102c65a03f1151561107b57600080fd5b50505033600160a060020a03166108fc611097610839846115b8565b9081150290604051600060405180830381858888f1935050505015156110bc57600080fd5b7fac16c1eed76505aa9d2f8db9df61e1e000d6c24f2c82b9d5ca3b1cbbc63b51018433604051918252600160a060020a031660208201526040908101905180910390a150505050565b6000805460a060020a900460ff161561111d57600080fd5b60735433600160a060020a0390811691161461113857600080fd5b600160a060020a038316151561114d57600080fd5b43606c5411806111605750611160611d69565b151561116b57600080fd5b81151561117757600080fd5b600160a060020a0383166000908152606660205260409020541561119a57600080fd5b606554606490106111aa57600080fd5b6111b6610839836115b8565b905034819010156111c657600080fd5b610ae4838383611d7e565b606d5481565b6101155481565b60666020526000908152604090205481565b6000600260a06109f9565b60005433600160a060020a0390811691161461121657600080fd5b60005460a860020a900460ff16151561122e57600080fd5b6000805475ff000000000000000000000000000000000000000000191681557f84f9bd54c70699971e701e07b4b61fc6c355ffbdfae18535c911b06a0adcf87390604051901515815260200160405180910390a1565b60005460a860020a900460ff1681565b60005433600160a060020a039081169116146112af57600080fd5b60005460a860020a900460ff16156112c657600080fd5b6000805475ff000000000000000000000000000000000000000000191660a860020a1790557f84f9bd54c70699971e701e07b4b61fc6c355ffbdfae18535c911b06a0adcf8736001604051901515815260200160405180910390a1565b606490565b60005433600160a060020a0390811691161461134357600080fd5b600160a060020a03811615610c4b5760008054600160a060020a03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b60655481565b600190565b6000805460a860020a900460ff16156113a257600080fd5b60735433600160a060020a039081169116146113bd57600080fd5b600160a060020a03841615156113d257600080fd5b606b5415806113e357506000607154115b15156113ee57600080fd5b6113f66109e2565b9050348190101561140657600080fd5b600160a060020a0384166000908152606660205260409020541561142957600080fd5b6005821461143657600080fd5b61143e6111f0565b606e541061144b57600080fd5b611453610ae9565b43101561145f57600080fd5b60705443111561146e57600080fd5b6114a78484848080602002602001604051908101604052809392919081815260200183836020028082843750889450611e549350505050565b50505050565b6114b56138da565b60006114bf6138da565b83915060008080846040518059106114d45750595b908082528060200260200182016040525093506114ef6116f4565b9150600090505b8481101561157f5761152588888381811061150d57fe5b9050602002013563ffffffff1663ffffffff16611cf6565b92506000831215611537576000611557565b81611555600163ffffffff86166064811061154e57fe5b0154611d41565b015b84828151811061156357fe5b63ffffffff9092166020928302909101909101526001016114f6565b50919695505050505050565b600068056bc75e2d631000006c0c9f2c9cd04674edea40000000835b068115156115b157fe5b0492915050565b6000620f42406305f5e100836115a7565b606b540290565b600080600160a060020a03841683156108fc0284604051600060405180830381858888f193505050509050801580156116095750600083115b1561167b57600160a060020a038416600090815260666020526040908190208054850190557fd9ca6e9100bce633810823c6c2f61e378ef9c8cdfdd9c76cd65627221bebae4e908590859051600160a060020a03909216825260208201526040908101905180910390a1829150611680565b600091505b5092915050565b61168f6138da565b6065546000816040518059106116a25750595b90808252806020026020018201604052509250600090505b818110156116ef57600181606481106116cf57fe5b01548382815181106116dd57fe5b602090810290910101526001016116ba565b505090565b60008043905080606c5411610b1157606854606c54610384919083030204600101610b14565b600080600080600080895195508515156117375760009550611841565b6117468a600060018903611f56565b60009150600093505b8584101561183d5789848151811061176357fe5b90602001906020020151945061177885612083565b925061178f8861178787611d41565b018a896120b3565b905081841180156117c657506117c6836117c08c60018803815181106117b157fe5b90602001906020020151612083565b836120d2565b156117e9576117de8a836001870385600101886120ea565b600282019150611832565b858460010110801561180c575061180c836117c08c87600101815181106117b157fe5b15611832576118248a838685600101886001016120ea565b600190930192600291909101905b60019093019261174f565b8195505b5050505050949350505050565b60008061185e8484606c546121aa565b6118688484612295565b4360685461038481151561187857fe5b0401606c55611885612313565b61188f8484612379565b607354909150600160a060020a031663dfba3be185856040518363ffffffff1660e060020a0281526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b838110156118fd5780820151838201526020016118e5565b505050509050019350505050600060405180830381600087803b151561192257600080fd5b6102c65a03f1151561193357600080fd5b5050507f509fa41742f7ecc729c95f067e92bb34c2c2a8762ca2b82cbaaa69b47b3f97578461196186612516565b85604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b838110156119ac578082015183820152602001611994565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156119eb5780820151838201526020016119d3565b505050509050019550505050505060405180910390a17f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb73606c5460405190815260200160405180910390a19392505050565b6000611a4983836115d0565b90506000811115610ae457606d805482019055505050565b600080611a6e84846125ba565b9050611a8081610e7083606a54611c9c565b949350505050565b6000600a826115b1565b6000611a9c6138da565b611aa46138da565b600080611aaf6125ed565b9350611ac08460016070540361265b565b9250611acb846128e6565b607354600160a060020a03166311196cc2856040518263ffffffff1660e060020a0281526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015611b2f578082015183820152602001611b17565b5050505090500192505050600060405180830381600087803b1515611b5357600080fd5b6102c65a03f11515611b6457600080fd5b50506000606e5550611b74612313565b6071805460009091559150611b8a82858561292a565b60728054607155600090559050611ba084612a4d565b7f733715aec32b2c4ecd8945862c2c46cdc069409a28cc15fe0006b1f28624e4fd848484604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611c0e578082015183820152602001611bf6565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015611c4d578082015183820152602001611c35565b505050509050019550505050505060405180910390a1949350505050565b6000816402540be4000a826001016402540be4000a84811515611c8a57fe5b06811515611c9457fe5b049392505050565b6000612710828402046702c68af0bb140000811115611ccf5783686c6b935b8bbd400000811515611cc957fe5b04611cd1565b825b905060008111611ce2576001611a80565b9392505050565b6000612710838302611c94565b606554600090815b81811015611d345783611d1e60018360648110611d1757fe5b015461158b565b1415611d2c57809250611d3a565b600101611cfe565b60001992505b5050919050565b60006c0c9f2c9cd04674edea40000000701d6329f1c35ca4bfabb9f5610000000000836115a7565b600080611d77610a55611687565b1592915050565b611d86612aac565b611d91826000612bc7565b606580546001818101909255919350839160648110611dac57fe5b01558260676000611dbc8561158b565b815260200190815260200160002060006101000a815481600160a060020a030219169083600160a060020a031602179055507fc2b2fad32d91531c90e8056ef63f1d434747dedea07141b9f55d40f49109dedb83611e198461158b565b836040518084600160a060020a0316600160a060020a03168152602001838152602001828152602001935050505060405180910390a1505050565b607180548201905560008080611e6985612c09565b9250611e7485612c4e565b606e8054600180820190925591935060029091029081019150839060749060a08110611e9c57fe5b01556001810190829060749060a08110611eb257fe5b01558560676000611ec38682611c6b565b815260200190815260200160002060006101000a815481600160a060020a030219169083600160a060020a031602179055507fb9d4cfdb0dd36bcac96d1125d536c92360ef0b6b8f2e7912505cd3c5d213a3068684866040518084600160a060020a0316600160a060020a03168152602001838152602001828152602001935050505060405180910390a1505050505050565b8181600081831415611f675761207b565b85600286860305860181518110611f7a57fe5b9060200190602002015190505b818313612055575b80868481518110611f9c57fe5b906020019060200201511015611fb757600190920191611f8f565b858281518110611fc357fe5b90602001906020020151811015611fe05760001990910190611fb7565b81831361205057858281518110611ff357fe5b9060200190602002015186848151811061200957fe5b9060200190602002015187858151811061201f57fe5b90602001906020020188858151811061203457fe5b6020908102909101019190915252600190920191600019909101905b611f87565b8185121561206857612068868684611f56565b8383121561207b5761207b868486611f56565b505050505050565b6000701d6329f1c35ca4bfabb9f561000000000074446c3b15f9926687d2c40534fdb564000000000000836115a7565b6000818385028115156120c257fe5b0460190260320190509392505050565b60008183018413158015611a80575050900390121590565b60008585815181106120f857fe5b90602001906020020151905085848151811061211057fe5b9060200190602002015186868151811061212657fe5b602090810290910101528086858151811061213d57fe5b6020908102909101015285838151811061215357fe5b90602001906020020151905085828151811061216b57fe5b9060200190602002015186848151811061218157fe5b602090810290910101528086838151811061219857fe5b60209081029091010152505050505050565b60008080805b8581101561228c578315806121c55750604984115b156121f4576121d385612c84565b40836040519182526020820152604090810190519081900390206000945092505b61224a87828151811061220357fe5b9060200190602002015188836001018151811061221c57fe5b90602001906020020151600a87900a6103e881028781151561223a57fe5b0681151561224457fe5b04612ca6565b915060008211801561225c5750600382105b151561226757600080fd5b600190930192600282141561228457612284878283600101612d5c565b6002016121b0565b50505050505050565b6000806000808486510393506122a96116f4565b9150600090505b8363ffffffff168110156123035785818601815181106122cc57fe5b9060200190602002015192506122ec83836122e686611d41565b01612bc7565b600182606481106122f957fe5b01556001016122b0565b50505063ffffffff166065555050565b606e5415801561232557504360705411155b156123775760685443906201518081151561233c57fe5b040160708190557fcb15847438c34cb62896ab708e638b62e7e6a08197569fe6e150eaaeb1d4aa469060405190815260200160405180910390a15b565b60008080808080808061238c8a8a6125ba565b935061239a84606a54611c9c565b92506123a583612dbf565b9150600090505b8881101561243f578981815181106123c057fe5b9060200190602002015195506123f06108398b83600101815181106123e157fe5b906020019060200201516115b8565b6123fc610839886115b8565b0196506124336067600061240f8961158b565b8152602081019190915260400160002054600160a060020a031661083e8985611ce9565b909401936002016123ac565b6073546124af90600160a060020a031663565a2e2c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561248b57600080fd5b6102c65a03f1151561249c57600080fd5b5050506040518051905061083e86612dce565b8501945060008511156124c557606d8054860190555b436124ce610ae9565b11156124eb576124dd84612ddf565b6071805490910190556124fe565b6124f484612ddf565b6072805490910190555b6125088484611ce9565b9a9950505050505050505050565b61251e6138da565b60008083519150816040518059106125335750595b90808252806020026020018201604052509250600090505b81811015611d3a576067600061257586848151811061256657fe5b9060200190602002015161158b565b8152602081019190915260400160002054600160a060020a031683828151811061259b57fe5b600160a060020a0390921660209283029091019091015260010161254b565b600080805b838110156125e5576125d96108398683815181106123e157fe5b909101906001016125bf565b509392505050565b6125f56138da565b606e546000816040518059106126085750595b90808252806020026020018201604052509250600090505b818110156116ef5760746001600283020160a0811061263b57fe5b015483828151811061264957fe5b60209081029091010152600101612620565b6126636138da565b6000806000806000806000808a519750876040518059106126815750595b9080825280602002602001820160405250985060009250600091505b87821015612874576126c38b83815181106126b457fe5b90602001906020020151612df0565b90975095505b6103e88983815181106126d857fe5b9060200190602002015163ffffffff161015612869578215806126fb5750604983115b1561272e5761270b828b03612c84565b404288016040519182526020820152604090810190519081900390209450600092505b6127598883600101106127415782612746565b826001015b898786600a0a6103e80287600a0a612e3e565b93506127b087878d858151811061276c57fe5b906020019060200201518e888151811061278257fe5b90602001906020020151600a88900a6103e881028b8115156127a057fe5b068115156127aa57fe5b04612e72565b9050606589826001146127c357856127c5565b835b815181106127cf57fe5b9060200190602002018181510163ffffffff16905250606489600183146127f657836127f8565b855b8151811061280257fe5b9060200190602002018181510163ffffffff169052506103e889858151811061282757fe5b9060200190602002015163ffffffff161061285e5760018803841015612856576128568b8a8660018c03612edd565b600019909701965b6001909201916126c9565b60019091019061269d565b8a519750600091505b878210156128d857606489838151811061289357fe5b9060200190602002015163ffffffff168115156128ac57fe5b068983815181106128b957fe5b63ffffffff90921660209283029091019091015260019091019061287d565b505050505050505092915050565b60008082519150600090505b81811015610ae45760746002820260a0811061290a57fe5b015483828151811061291857fe5b602090810290910101526001016128f2565b60008060008060008060008851955061294286612fb6565b6127100294506129558a61011654611c9c565b935061296084612fc3565b925060009150600090505b858110156129bd576129b189828151811061298257fe5b906020019060200201518b858b858151811061299a57fe5b9060200190602002015163ffffffff160288612fcf565b9091019060010161296b565b607354612a2d90600160a060020a031663565a2e2c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612a0957600080fd5b6102c65a03f11515612a1a57600080fd5b5050506040518051905061083e8c613020565b820191506000821115612a4357606d8054830190555b6125088a85611ce9565b60008082519150600090505b81811015610ae45760676000612a74858481518110610dc257fe5b8152602081019190915260400160002054600160a060020a0316838281518110612a9a57fe5b60209081029091010152600101612a59565b606554606c54439060008080848411612b66576000861115612b1557612ad06116f4565b9150600090505b85811015612b155760018160648110612aec57fe5b01549250612afe83836122e686611d41565b60018260648110612b0b57fe5b0155600101612ad7565b84606854610384811515612b2557fe5b0401606c81905593507f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb738460405190815260200160405180910390a161207b565b606486600101148015612b7c5750600a85018490105b1561207b5760058501606c81905593507f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb738460405190815260200160405180910390a1505050505050565b6000633b9aca008211612bda5781612be0565b633b9aca005b91506c0c9f2c9cd04674edea4000000080830290612bfd85611d41565b02840301905092915050565b600080808084519150600090505b81811015612c4557612c2e85828151811061256657fe5b6402540be40082900a029290920191600101612c17565b50909392505050565b600080600080600080612c6087613032565b939850919650945092509050612c7985858585856130fd565b979650505050505050565b600043610100808206820390840601818110611ce25761010081039250611d3a565b6000806000806000806000806000612cbd8c613149565b9750612cc88b613149565b9650612cd48c89613174565b9550612ce08b88613174565b94506064612cef8b600161318d565b612cfa8e898c6131ab565b02811515612d0457fe5b0593506064612d148b600a61318d565b612d1f8d888b6131ab565b02811515612d2957fe5b059250612d3a8c878a8e8a8d6132ae565b9092509050612d4c848484848e6132f5565b9c9b505050505050505050505050565b6000838381518110612d6a57fe5b906020019060200201519050838281518110612d8257fe5b90602001906020020151848481518110612d9857fe5b6020908102909101015280848381518110612daf57fe5b6020908102909101015250505050565b606f5460695461271003030390565b6069546000906127109083026115b1565b606f546000906127109083026115b1565b64e8d4a5100069d3c21bcecceda10000008206049173af298d050e4395d69670b12b7f41000000000000789f4f2726179a224501d762422c946590d910000000000000009092069190910490565b600085868603838587811515612e5057fe5b06811515612e5a57fe5b04811515612e6457fe5b060190505b95945050505050565b6000806000806000612e8388612df0565b9094509250612e928884613354565b9150612e9e878a613354565b90506125086064612eb088600161318d565b8c02811515612ebb57fe5b056064612ec989600a61318d565b8702811515612ed457fe5b0584848a6132f5565b6000848381518110612eeb57fe5b906020019060200201519050848281518110612f0357fe5b90602001906020020151858481518110612f1957fe5b6020908102909101015280858381518110612f3057fe5b60209081029091010152838381518110612f4657fe5b9060200190602002015163ffffffff169050838281518110612f6457fe5b90602001906020020151848481518110612f7a57fe5b63ffffffff90921660209283029091019091015280848381518110612f9b57fe5b63ffffffff9092166020928302909101909101525050505050565b60006002600a83026115b1565b61011554612710030390565b600080600080612fe0886000611c6b565b600081815260676020526040902054909350600160a060020a03169150613008878787613380565b905061301482826115d0565b98975050505050505050565b610115546000906127109083026115b1565b6000806000806000806000806000806000806000808e5198506130548f613398565b9094509250600090505b888110156130eb578e818151811061307257fe5b9060200190602002015191506130898285856131ab565b8d019c50613098828585613411565b9450848a136130a757846130a9565b895b99506130b6828585613491565b9f82019f91995097509550868c136130ce57866130d0565b8b5b9b50858b136130df57856130e1565b8a5b9a5060010161305e565b50505050505050505091939590929450565b73af298d050e4395d69670b12b7f4100000000000081026ec097ce7bc90715b34b9f1000000000830269d3c21bcecceda1000000850264e8d4a510008702880101010195945050505050565b600061316e600061316961316461315f86613554565b61356d565b613594565b6135a1565b92915050565b6000611ce26131876000613169866135a8565b836135c3565b60006131a16000600b858560640286612e3e565b6064019392505050565b6000806000806000806000806131c08b6135c9565b96506131ce61315f8c613554565b95506131de600788888d8d6135d6565b620f42406131eb8d61373d565b020194506131fd600888888d8d6135d6565b620f424061320a8d613751565b0201935061321c600388888d8d6135d6565b600485056005870501019250613236600488888d8d6135d6565b60198505620f424001019150613250600988888d8d6135d6565b6002620f424061325f8e613764565b6003020281151561326c57fe5b05019050620f42406305f5e10084840205620f4240016132948361328f8f6115b8565b61377b565b0281151561329e57fe5b059b9a5050505050505050505050565b60008060006132be868686613411565b90506132cc898989846137c5565b92506132d9898989613411565b90506132e7868989846137c5565b915050965096945050505050565b60008464e8d4a51000850281151561330957fe5b0593508564e8d4a51000840281151561331e57fe5b05925082841415613336575060016002820601612e69565b828413613344576002613347565b60015b60ff169695505050505050565b600080600080613363866137e9565b9194509250905061337683838388613830565b9695505050505050565b60008183850281151561338f57fe5b04949350505050565b6000806000806000855192506000945060009350600090505b828110156133fd578581815181106133c557fe5b9060200190602002015191506133de85613169846135a8565b94506133f38461316961316461315f86613554565b93506001016133b1565b61340785856135c3565b9450505050915091565b6000806000806000613422886135c9565b935061343061315f89613554565b9250613440600785858a8a6135d6565b620f424061344d8a61373d565b0201915061345f600885858a8a6135d6565b620f424061346c8a613751565b0201905061347e600585858a8a6135d6565b6002909102909101019695505050505050565b60008060008060008060006134a58a6135c9565b93506134b361315f8b613554565b92506134c3600685858c8c6135d6565b620f42406134d08c6138a4565b020191506134e2600785858c8c6135d6565b620f42406134ef8c61373d565b02019050613501600185858c8c6135d6565b600a60078402058301016305f5e1000196506135208761328f8c6115b8565b9650613530600285858c8c6135d6565b81600a60088502058401010195506003600282020594505050505093509350939050565b6000662386f26fc10000670de0b6b3a7640000836115a7565b6000816103e80a826001016103e80a6d0e2885b04902c576218a2b72bc608115156115a757fe5b600060646103e8836115a7565b60020a1790565b6000670de0b6b3a764000068056bc75e2d63100000836115a7565b81161890565b6000600a612710836115a7565b6000808686141561361057620f42408760640a8860010160640a67dee2bc31128b5a8081151561360257fe5b0681151561360c57fe5b0402015b613619856138b9565b87141561365b576002620f42408860640a8960010160640a67dee2bc31128b5a8081151561364357fe5b0681151561364d57fe5b040281151561365857fe5b05015b613664856138c5565b8714156136a6576002620f42408860640a8960010160640a67dee2bc31128b5a8081151561368e57fe5b0681151561369857fe5b04028115156136a357fe5b05015b6136b3848860ff166138d1565b156136f3576002620f42408860640a8960010160640a67dee2bc31128b5a808115156136db57fe5b068115156136e557fe5b04028115156136f057fe5b05015b613700838860ff166138d1565b1561337657620f42408760640a8860010160640a67dee2bc31128b5a8081151561372657fe5b0681151561373057fe5b0402019695505050505050565b60006402540be40064e8d4a51000836115a7565b60006305f5e1006402540be400836115a7565b6000655af3107a4000662386f26fc10000836115a7565b6000600182039150600a8260640a8360010160640a7443ab3b8790243acda5750d30b685bd5fbe5056b0568115156137af57fe5b068115156137b957fe5b048402811515611c9457fe5b6000806000806137d6888888613491565b9194509250905061301483838388613830565b64e8d4a5100081069169d3c21bcecceda10000006ec097ce7bc90715b34b9f1000000000808406919091049273af298d050e4395d69670b12b7f4100000000000090060490565b6000808063047868bf198487031261384a57838603613851565b63047868bf195b91506064808305620f424001620f4240840281151561386c57fe5b0581151561387657fe5b05620f42400390506305f5e1008786020581620f4240890281151561389757fe5b0501979650505050505050565b600064e8d4a51000655af3107a4000836115a7565b6000600a6064836115a7565b60006001600a836115a7565b60020a16151590565b602060405190810160405260008152905600a165627a7a7230582099dac81d836679d8342f2783f1aa770750dcadc0549ce873acaf5c98c9a5f5b70029000000000000000000000000d79b3d20bf9e9d54c4da992fda0b52861fb6757500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000007d000000000000000000000000000000000000000000000000000000000000001f4

Deployed Bytecode

0x6060604052600436106102425763ffffffff60e060020a600035041663059d45da81146102475780630c26a8161461025c5780630ca435a61461028157806315834aed146102945780631924f74a146102aa5780631ba5e63f146102bd5780631c44bac7146102d05780631ca43cef146102e357806320154e78146102f657806324abfc02146103095780632a6c703b1461031c5780634474809f1461032f5780634619aa191461034257806346682cab14610355578063477e5d241461037c5780634a7761041461039257806351c85565146103a5578063534e3f81146103b857806354070f83146103cb5780635663896e146103de57806359bb7867146103f45780635c7d64251461040a5780636147bcb914610420578063637492251461045e5780636b2bb92a146104745780636f6f828e1461048757806374af3ee11461049a5780637a314ce2146104df5780637ced55c2146104f257806389cda6a8146105055780638da5cb5b146105185780638db7cd54146105475780639d619dad1461055a578063a43b92a21461056d578063a48255b714610580578063a72f3dea14610593578063a99306e7146105a9578063af10b8cf146105c0578063b3a810f7146105d3578063bd1954c6146105e6578063c6169e1f14610605578063d08279b014610618578063d34468561461062b578063d8a489d91461063e578063f15a9ae814610651578063f2fde38b14610664578063f73c8dfd14610683578063f773ab8014610696578063f8b608a1146106a9578063fe1b602a146106c9575b600080fd5b341561025257600080fd5b61025a61073a565b005b341561026757600080fd5b61026f61085f565b60405190815260200160405180910390f35b341561028c57600080fd5b61025a610865565b341561029f57600080fd5b61026f60043561092e565b34156102b557600080fd5b61026f610942565b34156102c857600080fd5b61026f610948565b34156102db57600080fd5b61026f61094e565b34156102ee57600080fd5b61026f610954565b341561030157600080fd5b61025a61095a565b341561031457600080fd5b61026f6109e2565b341561032757600080fd5b61026f6109ff565b341561033a57600080fd5b61026f610a05565b341561034d57600080fd5b61026f610a0b565b341561036057600080fd5b610368610a11565b604051901515815260200160405180910390f35b341561038757600080fd5b61026f600435610a2c565b341561039d57600080fd5b610368610a39565b34156103b057600080fd5b61026f610a6e565b34156103c357600080fd5b61025a610a74565b34156103d657600080fd5b61026f610ae9565b34156103e957600080fd5b61025a600435610b1a565b34156103ff57600080fd5b61025a600435610b3a565b341561041557600080fd5b61025a600435610b77565b341561042b57600080fd5b610433610ba4565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b341561046957600080fd5b61026f600435610bea565b341561047f57600080fd5b61025a610bff565b341561049257600080fd5b61025a610c4e565b34156104a557600080fd5b6104ad610e46565b604051808681526020018581526020018481526020018381526020018281526020019550505050505060405180910390f35b34156104ea57600080fd5b61026f610e7e565b34156104fd57600080fd5b610368610e85565b341561051057600080fd5b61025a610e95565b341561052357600080fd5b61052b610f23565b604051600160a060020a03909116815260200160405180910390f35b341561055257600080fd5b61026f610f32565b341561056557600080fd5b61026f610f46565b341561057857600080fd5b61026f610f4c565b341561058b57600080fd5b61026f610f53565b341561059e57600080fd5b61025a600435610f59565b61025a600160a060020a0360043516602435611105565b34156105cb57600080fd5b61026f6111d1565b34156105de57600080fd5b61026f6111d7565b34156105f157600080fd5b61026f600160a060020a03600435166111de565b341561061057600080fd5b61026f6111f0565b341561062357600080fd5b61025a6111fb565b341561063657600080fd5b610368611284565b341561064957600080fd5b61025a611294565b341561065c57600080fd5b61026f611323565b341561066f57600080fd5b61025a600160a060020a0360043516611328565b341561068e57600080fd5b61026f61137f565b34156106a157600080fd5b610368611385565b61025a60048035600160a060020a0316906024803590810191013561138a565b34156106d457600080fd5b6106e760048035602481019101356114ad565b60405160208082528190810183818151815260200191508051906020019060200280838360005b8381101561072657808201518382015260200161070e565b505050509050019250505060405180910390f35b600080548190819081908190819033600160a060020a0390811691161461076057600080fd5b60005460a060020a900460ff16151561077857600080fd5b506065805460009182905595505b8581101561084f576001816064811061079b57fe5b015494506107a88561158b565b607354909450600160a060020a031663d7d3c4d5856040518263ffffffff1660e060020a028152600401808263ffffffff168152602001915050600060405180830381600087803b15156107fb57600080fd5b6102c65a03f1151561080c57600080fd5b505050600084815260676020526040902054600160a060020a031691506108438261083e610839886115b8565b6115c9565b6115d0565b90920191600101610786565b5050606d80549091019055505050565b606a5481565b600160a060020a03331660009081526066602052604081205490811161088a57600080fd5b606d548190101561089a57600080fd5b600160a060020a03331660008181526066602052604080822091909155606d8054849003905582156108fc0290839051600060405180830381858888f1935050505015156108e757600080fd5b7fd7a49720c00abce919ff2e9064ad8b7c58f0f539d778c4442b4c86422462d11b3382604051600160a060020a03909216825260208201526040908101905180910390a150565b60748160a0811061093b57fe5b0154905081565b61038481565b606e5481565b606f5481565b61a8c081565b60005433600160a060020a0390811691161461097557600080fd5b60005460a060020a900460ff16151561098d57600080fd5b6000805474ff0000000000000000000000000000000000000000191681557f4e494720a65633d819abbff3aa9af070f5451ce37f40dcdbb5b78b6bbbd8eac490604051901515815260200160405180910390a1565b6101145460715460009161271091026125e4028190045b04905090565b610e1081565b60705481565b60695481565b60004360705411158015610a2757506000606e54115b905090565b6001816064811061093b57fe5b600043606c5411158015610a2757506001610a68610a55611687565b610384610a606116f4565b610e1061171a565b11905090565b60725481565b610a7c6138da565b60008054819060a060020a900460ff1615610a9657600080fd5b606c5443901115610aa657600080fd5b610aae611687565b9250610abf83610384610a606116f4565b915060018211610ace57600080fd5b610ad8838361184e565b9050610ae43382611a3d565b505050565b60008060685461a8c0811515610afb57fe5b0490508060705410610b11578060705403610b14565b60005b91505090565b60005433600160a060020a03908116911614610b3557600080fd5b606855565b60005433600160a060020a03908116911614610b5557600080fd5b612710811115610b6457600080fd5b606e5415610b7157600080fd5b61011455565b60005433600160a060020a03908116911614610b9257600080fd5b60655415610b9f57600080fd5b606b55565b600080600080610bb26138da565b610bba611687565b90506065549450610bd081610384610a606116f4565b9350606c549250610be18185611a61565b91505090919293565b6000610bf582611a88565b606b540292915050565b6000805460a860020a900460ff1615610c1757600080fd5b60705443901115610c2757600080fd5b606e5460009011610c3757600080fd5b610c3f611a92565b9050610c4b3382611a3d565b50565b600080600080600080610c5f6138da565b60005433600160a060020a03908116911614610c7a57600080fd5b60005460a860020a900460ff161515610c9257600080fd5b606e549650607154915086604051805910610caa5750595b90808252806020026020018201604052509050600093505b86841015610cfd5760746002850260a08110610cda57fe5b0154818581518110610ce857fe5b60209081029091010152600190930192610cc2565b607354600160a060020a03166311196cc2826040518263ffffffff1660e060020a0281526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d61578082015183820152602001610d49565b5050505090500192505050600060405180830381600087803b1515610d8557600080fd5b6102c65a03f11515610d9657600080fd5b505060006071819055606e5550600019870193505b60008410610e3357610dd3818581518110610dc257fe5b906020019060200201516000611c6b565b61011454909650612710906125e40204612710018261271002811515610df557fe5b60008881526067602052604090205491900483039350610e1e90600160a060020a0316846115d0565b90940193600019909301929082900390610dab565b506071555050606d805490910190555050565b606e5460715460008080610e58610ae9565b92506070549150610e7584610e708661011654611c9c565b611ce9565b90509091929394565b6201518081565b60005460a060020a900460ff1681565b60005433600160a060020a03908116911614610eb057600080fd5b60005460a060020a900460ff1615610ec757600080fd5b6000805474ff0000000000000000000000000000000000000000191660a060020a1790557f4e494720a65633d819abbff3aa9af070f5451ce37f40dcdbb5b78b6bbbd8eac46001604051901515815260200160405180910390a1565b600054600160a060020a031681565b6101145460715460009161271091026109f9565b60715481565b6101165481565b606c5481565b606554600080808311610f6b57600080fd5b60008481526067602052604090205433600160a060020a03908116911614610f9257600080fd5b610f9b84611cf6565b91506000821215610fab57600080fd5b600163ffffffff831660648110610fbe57fe5b015490506004610fcc6116f4565b610fd583611d41565b011015610fe157600080fd5b6001830382101561101857606554600190600019016064811061100057fe5b0154600163ffffffff84166064811061101557fe5b01555b60658054600019019055607354600160a060020a031663d7d3c4d58560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561106a57600080fd5b6102c65a03f1151561107b57600080fd5b50505033600160a060020a03166108fc611097610839846115b8565b9081150290604051600060405180830381858888f1935050505015156110bc57600080fd5b7fac16c1eed76505aa9d2f8db9df61e1e000d6c24f2c82b9d5ca3b1cbbc63b51018433604051918252600160a060020a031660208201526040908101905180910390a150505050565b6000805460a060020a900460ff161561111d57600080fd5b60735433600160a060020a0390811691161461113857600080fd5b600160a060020a038316151561114d57600080fd5b43606c5411806111605750611160611d69565b151561116b57600080fd5b81151561117757600080fd5b600160a060020a0383166000908152606660205260409020541561119a57600080fd5b606554606490106111aa57600080fd5b6111b6610839836115b8565b905034819010156111c657600080fd5b610ae4838383611d7e565b606d5481565b6101155481565b60666020526000908152604090205481565b6000600260a06109f9565b60005433600160a060020a0390811691161461121657600080fd5b60005460a860020a900460ff16151561122e57600080fd5b6000805475ff000000000000000000000000000000000000000000191681557f84f9bd54c70699971e701e07b4b61fc6c355ffbdfae18535c911b06a0adcf87390604051901515815260200160405180910390a1565b60005460a860020a900460ff1681565b60005433600160a060020a039081169116146112af57600080fd5b60005460a860020a900460ff16156112c657600080fd5b6000805475ff000000000000000000000000000000000000000000191660a860020a1790557f84f9bd54c70699971e701e07b4b61fc6c355ffbdfae18535c911b06a0adcf8736001604051901515815260200160405180910390a1565b606490565b60005433600160a060020a0390811691161461134357600080fd5b600160a060020a03811615610c4b5760008054600160a060020a03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b60655481565b600190565b6000805460a860020a900460ff16156113a257600080fd5b60735433600160a060020a039081169116146113bd57600080fd5b600160a060020a03841615156113d257600080fd5b606b5415806113e357506000607154115b15156113ee57600080fd5b6113f66109e2565b9050348190101561140657600080fd5b600160a060020a0384166000908152606660205260409020541561142957600080fd5b6005821461143657600080fd5b61143e6111f0565b606e541061144b57600080fd5b611453610ae9565b43101561145f57600080fd5b60705443111561146e57600080fd5b6114a78484848080602002602001604051908101604052809392919081815260200183836020028082843750889450611e549350505050565b50505050565b6114b56138da565b60006114bf6138da565b83915060008080846040518059106114d45750595b908082528060200260200182016040525093506114ef6116f4565b9150600090505b8481101561157f5761152588888381811061150d57fe5b9050602002013563ffffffff1663ffffffff16611cf6565b92506000831215611537576000611557565b81611555600163ffffffff86166064811061154e57fe5b0154611d41565b015b84828151811061156357fe5b63ffffffff9092166020928302909101909101526001016114f6565b50919695505050505050565b600068056bc75e2d631000006c0c9f2c9cd04674edea40000000835b068115156115b157fe5b0492915050565b6000620f42406305f5e100836115a7565b606b540290565b600080600160a060020a03841683156108fc0284604051600060405180830381858888f193505050509050801580156116095750600083115b1561167b57600160a060020a038416600090815260666020526040908190208054850190557fd9ca6e9100bce633810823c6c2f61e378ef9c8cdfdd9c76cd65627221bebae4e908590859051600160a060020a03909216825260208201526040908101905180910390a1829150611680565b600091505b5092915050565b61168f6138da565b6065546000816040518059106116a25750595b90808252806020026020018201604052509250600090505b818110156116ef57600181606481106116cf57fe5b01548382815181106116dd57fe5b602090810290910101526001016116ba565b505090565b60008043905080606c5411610b1157606854606c54610384919083030204600101610b14565b600080600080600080895195508515156117375760009550611841565b6117468a600060018903611f56565b60009150600093505b8584101561183d5789848151811061176357fe5b90602001906020020151945061177885612083565b925061178f8861178787611d41565b018a896120b3565b905081841180156117c657506117c6836117c08c60018803815181106117b157fe5b90602001906020020151612083565b836120d2565b156117e9576117de8a836001870385600101886120ea565b600282019150611832565b858460010110801561180c575061180c836117c08c87600101815181106117b157fe5b15611832576118248a838685600101886001016120ea565b600190930192600291909101905b60019093019261174f565b8195505b5050505050949350505050565b60008061185e8484606c546121aa565b6118688484612295565b4360685461038481151561187857fe5b0401606c55611885612313565b61188f8484612379565b607354909150600160a060020a031663dfba3be185856040518363ffffffff1660e060020a0281526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b838110156118fd5780820151838201526020016118e5565b505050509050019350505050600060405180830381600087803b151561192257600080fd5b6102c65a03f1151561193357600080fd5b5050507f509fa41742f7ecc729c95f067e92bb34c2c2a8762ca2b82cbaaa69b47b3f97578461196186612516565b85604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b838110156119ac578082015183820152602001611994565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156119eb5780820151838201526020016119d3565b505050509050019550505050505060405180910390a17f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb73606c5460405190815260200160405180910390a19392505050565b6000611a4983836115d0565b90506000811115610ae457606d805482019055505050565b600080611a6e84846125ba565b9050611a8081610e7083606a54611c9c565b949350505050565b6000600a826115b1565b6000611a9c6138da565b611aa46138da565b600080611aaf6125ed565b9350611ac08460016070540361265b565b9250611acb846128e6565b607354600160a060020a03166311196cc2856040518263ffffffff1660e060020a0281526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015611b2f578082015183820152602001611b17565b5050505090500192505050600060405180830381600087803b1515611b5357600080fd5b6102c65a03f11515611b6457600080fd5b50506000606e5550611b74612313565b6071805460009091559150611b8a82858561292a565b60728054607155600090559050611ba084612a4d565b7f733715aec32b2c4ecd8945862c2c46cdc069409a28cc15fe0006b1f28624e4fd848484604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611c0e578082015183820152602001611bf6565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015611c4d578082015183820152602001611c35565b505050509050019550505050505060405180910390a1949350505050565b6000816402540be4000a826001016402540be4000a84811515611c8a57fe5b06811515611c9457fe5b049392505050565b6000612710828402046702c68af0bb140000811115611ccf5783686c6b935b8bbd400000811515611cc957fe5b04611cd1565b825b905060008111611ce2576001611a80565b9392505050565b6000612710838302611c94565b606554600090815b81811015611d345783611d1e60018360648110611d1757fe5b015461158b565b1415611d2c57809250611d3a565b600101611cfe565b60001992505b5050919050565b60006c0c9f2c9cd04674edea40000000701d6329f1c35ca4bfabb9f5610000000000836115a7565b600080611d77610a55611687565b1592915050565b611d86612aac565b611d91826000612bc7565b606580546001818101909255919350839160648110611dac57fe5b01558260676000611dbc8561158b565b815260200190815260200160002060006101000a815481600160a060020a030219169083600160a060020a031602179055507fc2b2fad32d91531c90e8056ef63f1d434747dedea07141b9f55d40f49109dedb83611e198461158b565b836040518084600160a060020a0316600160a060020a03168152602001838152602001828152602001935050505060405180910390a1505050565b607180548201905560008080611e6985612c09565b9250611e7485612c4e565b606e8054600180820190925591935060029091029081019150839060749060a08110611e9c57fe5b01556001810190829060749060a08110611eb257fe5b01558560676000611ec38682611c6b565b815260200190815260200160002060006101000a815481600160a060020a030219169083600160a060020a031602179055507fb9d4cfdb0dd36bcac96d1125d536c92360ef0b6b8f2e7912505cd3c5d213a3068684866040518084600160a060020a0316600160a060020a03168152602001838152602001828152602001935050505060405180910390a1505050505050565b8181600081831415611f675761207b565b85600286860305860181518110611f7a57fe5b9060200190602002015190505b818313612055575b80868481518110611f9c57fe5b906020019060200201511015611fb757600190920191611f8f565b858281518110611fc357fe5b90602001906020020151811015611fe05760001990910190611fb7565b81831361205057858281518110611ff357fe5b9060200190602002015186848151811061200957fe5b9060200190602002015187858151811061201f57fe5b90602001906020020188858151811061203457fe5b6020908102909101019190915252600190920191600019909101905b611f87565b8185121561206857612068868684611f56565b8383121561207b5761207b868486611f56565b505050505050565b6000701d6329f1c35ca4bfabb9f561000000000074446c3b15f9926687d2c40534fdb564000000000000836115a7565b6000818385028115156120c257fe5b0460190260320190509392505050565b60008183018413158015611a80575050900390121590565b60008585815181106120f857fe5b90602001906020020151905085848151811061211057fe5b9060200190602002015186868151811061212657fe5b602090810290910101528086858151811061213d57fe5b6020908102909101015285838151811061215357fe5b90602001906020020151905085828151811061216b57fe5b9060200190602002015186848151811061218157fe5b602090810290910101528086838151811061219857fe5b60209081029091010152505050505050565b60008080805b8581101561228c578315806121c55750604984115b156121f4576121d385612c84565b40836040519182526020820152604090810190519081900390206000945092505b61224a87828151811061220357fe5b9060200190602002015188836001018151811061221c57fe5b90602001906020020151600a87900a6103e881028781151561223a57fe5b0681151561224457fe5b04612ca6565b915060008211801561225c5750600382105b151561226757600080fd5b600190930192600282141561228457612284878283600101612d5c565b6002016121b0565b50505050505050565b6000806000808486510393506122a96116f4565b9150600090505b8363ffffffff168110156123035785818601815181106122cc57fe5b9060200190602002015192506122ec83836122e686611d41565b01612bc7565b600182606481106122f957fe5b01556001016122b0565b50505063ffffffff166065555050565b606e5415801561232557504360705411155b156123775760685443906201518081151561233c57fe5b040160708190557fcb15847438c34cb62896ab708e638b62e7e6a08197569fe6e150eaaeb1d4aa469060405190815260200160405180910390a15b565b60008080808080808061238c8a8a6125ba565b935061239a84606a54611c9c565b92506123a583612dbf565b9150600090505b8881101561243f578981815181106123c057fe5b9060200190602002015195506123f06108398b83600101815181106123e157fe5b906020019060200201516115b8565b6123fc610839886115b8565b0196506124336067600061240f8961158b565b8152602081019190915260400160002054600160a060020a031661083e8985611ce9565b909401936002016123ac565b6073546124af90600160a060020a031663565a2e2c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561248b57600080fd5b6102c65a03f1151561249c57600080fd5b5050506040518051905061083e86612dce565b8501945060008511156124c557606d8054860190555b436124ce610ae9565b11156124eb576124dd84612ddf565b6071805490910190556124fe565b6124f484612ddf565b6072805490910190555b6125088484611ce9565b9a9950505050505050505050565b61251e6138da565b60008083519150816040518059106125335750595b90808252806020026020018201604052509250600090505b81811015611d3a576067600061257586848151811061256657fe5b9060200190602002015161158b565b8152602081019190915260400160002054600160a060020a031683828151811061259b57fe5b600160a060020a0390921660209283029091019091015260010161254b565b600080805b838110156125e5576125d96108398683815181106123e157fe5b909101906001016125bf565b509392505050565b6125f56138da565b606e546000816040518059106126085750595b90808252806020026020018201604052509250600090505b818110156116ef5760746001600283020160a0811061263b57fe5b015483828151811061264957fe5b60209081029091010152600101612620565b6126636138da565b6000806000806000806000808a519750876040518059106126815750595b9080825280602002602001820160405250985060009250600091505b87821015612874576126c38b83815181106126b457fe5b90602001906020020151612df0565b90975095505b6103e88983815181106126d857fe5b9060200190602002015163ffffffff161015612869578215806126fb5750604983115b1561272e5761270b828b03612c84565b404288016040519182526020820152604090810190519081900390209450600092505b6127598883600101106127415782612746565b826001015b898786600a0a6103e80287600a0a612e3e565b93506127b087878d858151811061276c57fe5b906020019060200201518e888151811061278257fe5b90602001906020020151600a88900a6103e881028b8115156127a057fe5b068115156127aa57fe5b04612e72565b9050606589826001146127c357856127c5565b835b815181106127cf57fe5b9060200190602002018181510163ffffffff16905250606489600183146127f657836127f8565b855b8151811061280257fe5b9060200190602002018181510163ffffffff169052506103e889858151811061282757fe5b9060200190602002015163ffffffff161061285e5760018803841015612856576128568b8a8660018c03612edd565b600019909701965b6001909201916126c9565b60019091019061269d565b8a519750600091505b878210156128d857606489838151811061289357fe5b9060200190602002015163ffffffff168115156128ac57fe5b068983815181106128b957fe5b63ffffffff90921660209283029091019091015260019091019061287d565b505050505050505092915050565b60008082519150600090505b81811015610ae45760746002820260a0811061290a57fe5b015483828151811061291857fe5b602090810290910101526001016128f2565b60008060008060008060008851955061294286612fb6565b6127100294506129558a61011654611c9c565b935061296084612fc3565b925060009150600090505b858110156129bd576129b189828151811061298257fe5b906020019060200201518b858b858151811061299a57fe5b9060200190602002015163ffffffff160288612fcf565b9091019060010161296b565b607354612a2d90600160a060020a031663565a2e2c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612a0957600080fd5b6102c65a03f11515612a1a57600080fd5b5050506040518051905061083e8c613020565b820191506000821115612a4357606d8054830190555b6125088a85611ce9565b60008082519150600090505b81811015610ae45760676000612a74858481518110610dc257fe5b8152602081019190915260400160002054600160a060020a0316838281518110612a9a57fe5b60209081029091010152600101612a59565b606554606c54439060008080848411612b66576000861115612b1557612ad06116f4565b9150600090505b85811015612b155760018160648110612aec57fe5b01549250612afe83836122e686611d41565b60018260648110612b0b57fe5b0155600101612ad7565b84606854610384811515612b2557fe5b0401606c81905593507f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb738460405190815260200160405180910390a161207b565b606486600101148015612b7c5750600a85018490105b1561207b5760058501606c81905593507f9a6ed90ac7336988045822a7b73c3b1d3570646a0837176eb0a8c3b61de8eb738460405190815260200160405180910390a1505050505050565b6000633b9aca008211612bda5781612be0565b633b9aca005b91506c0c9f2c9cd04674edea4000000080830290612bfd85611d41565b02840301905092915050565b600080808084519150600090505b81811015612c4557612c2e85828151811061256657fe5b6402540be40082900a029290920191600101612c17565b50909392505050565b600080600080600080612c6087613032565b939850919650945092509050612c7985858585856130fd565b979650505050505050565b600043610100808206820390840601818110611ce25761010081039250611d3a565b6000806000806000806000806000612cbd8c613149565b9750612cc88b613149565b9650612cd48c89613174565b9550612ce08b88613174565b94506064612cef8b600161318d565b612cfa8e898c6131ab565b02811515612d0457fe5b0593506064612d148b600a61318d565b612d1f8d888b6131ab565b02811515612d2957fe5b059250612d3a8c878a8e8a8d6132ae565b9092509050612d4c848484848e6132f5565b9c9b505050505050505050505050565b6000838381518110612d6a57fe5b906020019060200201519050838281518110612d8257fe5b90602001906020020151848481518110612d9857fe5b6020908102909101015280848381518110612daf57fe5b6020908102909101015250505050565b606f5460695461271003030390565b6069546000906127109083026115b1565b606f546000906127109083026115b1565b64e8d4a5100069d3c21bcecceda10000008206049173af298d050e4395d69670b12b7f41000000000000789f4f2726179a224501d762422c946590d910000000000000009092069190910490565b600085868603838587811515612e5057fe5b06811515612e5a57fe5b04811515612e6457fe5b060190505b95945050505050565b6000806000806000612e8388612df0565b9094509250612e928884613354565b9150612e9e878a613354565b90506125086064612eb088600161318d565b8c02811515612ebb57fe5b056064612ec989600a61318d565b8702811515612ed457fe5b0584848a6132f5565b6000848381518110612eeb57fe5b906020019060200201519050848281518110612f0357fe5b90602001906020020151858481518110612f1957fe5b6020908102909101015280858381518110612f3057fe5b60209081029091010152838381518110612f4657fe5b9060200190602002015163ffffffff169050838281518110612f6457fe5b90602001906020020151848481518110612f7a57fe5b63ffffffff90921660209283029091019091015280848381518110612f9b57fe5b63ffffffff9092166020928302909101909101525050505050565b60006002600a83026115b1565b61011554612710030390565b600080600080612fe0886000611c6b565b600081815260676020526040902054909350600160a060020a03169150613008878787613380565b905061301482826115d0565b98975050505050505050565b610115546000906127109083026115b1565b6000806000806000806000806000806000806000808e5198506130548f613398565b9094509250600090505b888110156130eb578e818151811061307257fe5b9060200190602002015191506130898285856131ab565b8d019c50613098828585613411565b9450848a136130a757846130a9565b895b99506130b6828585613491565b9f82019f91995097509550868c136130ce57866130d0565b8b5b9b50858b136130df57856130e1565b8a5b9a5060010161305e565b50505050505050505091939590929450565b73af298d050e4395d69670b12b7f4100000000000081026ec097ce7bc90715b34b9f1000000000830269d3c21bcecceda1000000850264e8d4a510008702880101010195945050505050565b600061316e600061316961316461315f86613554565b61356d565b613594565b6135a1565b92915050565b6000611ce26131876000613169866135a8565b836135c3565b60006131a16000600b858560640286612e3e565b6064019392505050565b6000806000806000806000806131c08b6135c9565b96506131ce61315f8c613554565b95506131de600788888d8d6135d6565b620f42406131eb8d61373d565b020194506131fd600888888d8d6135d6565b620f424061320a8d613751565b0201935061321c600388888d8d6135d6565b600485056005870501019250613236600488888d8d6135d6565b60198505620f424001019150613250600988888d8d6135d6565b6002620f424061325f8e613764565b6003020281151561326c57fe5b05019050620f42406305f5e10084840205620f4240016132948361328f8f6115b8565b61377b565b0281151561329e57fe5b059b9a5050505050505050505050565b60008060006132be868686613411565b90506132cc898989846137c5565b92506132d9898989613411565b90506132e7868989846137c5565b915050965096945050505050565b60008464e8d4a51000850281151561330957fe5b0593508564e8d4a51000840281151561331e57fe5b05925082841415613336575060016002820601612e69565b828413613344576002613347565b60015b60ff169695505050505050565b600080600080613363866137e9565b9194509250905061337683838388613830565b9695505050505050565b60008183850281151561338f57fe5b04949350505050565b6000806000806000855192506000945060009350600090505b828110156133fd578581815181106133c557fe5b9060200190602002015191506133de85613169846135a8565b94506133f38461316961316461315f86613554565b93506001016133b1565b61340785856135c3565b9450505050915091565b6000806000806000613422886135c9565b935061343061315f89613554565b9250613440600785858a8a6135d6565b620f424061344d8a61373d565b0201915061345f600885858a8a6135d6565b620f424061346c8a613751565b0201905061347e600585858a8a6135d6565b6002909102909101019695505050505050565b60008060008060008060006134a58a6135c9565b93506134b361315f8b613554565b92506134c3600685858c8c6135d6565b620f42406134d08c6138a4565b020191506134e2600785858c8c6135d6565b620f42406134ef8c61373d565b02019050613501600185858c8c6135d6565b600a60078402058301016305f5e1000196506135208761328f8c6115b8565b9650613530600285858c8c6135d6565b81600a60088502058401010195506003600282020594505050505093509350939050565b6000662386f26fc10000670de0b6b3a7640000836115a7565b6000816103e80a826001016103e80a6d0e2885b04902c576218a2b72bc608115156115a757fe5b600060646103e8836115a7565b60020a1790565b6000670de0b6b3a764000068056bc75e2d63100000836115a7565b81161890565b6000600a612710836115a7565b6000808686141561361057620f42408760640a8860010160640a67dee2bc31128b5a8081151561360257fe5b0681151561360c57fe5b0402015b613619856138b9565b87141561365b576002620f42408860640a8960010160640a67dee2bc31128b5a8081151561364357fe5b0681151561364d57fe5b040281151561365857fe5b05015b613664856138c5565b8714156136a6576002620f42408860640a8960010160640a67dee2bc31128b5a8081151561368e57fe5b0681151561369857fe5b04028115156136a357fe5b05015b6136b3848860ff166138d1565b156136f3576002620f42408860640a8960010160640a67dee2bc31128b5a808115156136db57fe5b068115156136e557fe5b04028115156136f057fe5b05015b613700838860ff166138d1565b1561337657620f42408760640a8860010160640a67dee2bc31128b5a8081151561372657fe5b0681151561373057fe5b0402019695505050505050565b60006402540be40064e8d4a51000836115a7565b60006305f5e1006402540be400836115a7565b6000655af3107a4000662386f26fc10000836115a7565b6000600182039150600a8260640a8360010160640a7443ab3b8790243acda5750d30b685bd5fbe5056b0568115156137af57fe5b068115156137b957fe5b048402811515611c9457fe5b6000806000806137d6888888613491565b9194509250905061301483838388613830565b64e8d4a5100081069169d3c21bcecceda10000006ec097ce7bc90715b34b9f1000000000808406919091049273af298d050e4395d69670b12b7f4100000000000090060490565b6000808063047868bf198487031261384a57838603613851565b63047868bf195b91506064808305620f424001620f4240840281151561386c57fe5b0581151561387657fe5b05620f42400390506305f5e1008786020581620f4240890281151561389757fe5b0501979650505050505050565b600064e8d4a51000655af3107a4000836115a7565b6000600a6064836115a7565b60006001600a836115a7565b60020a16151590565b602060405190810160405260008152905600a165627a7a7230582099dac81d836679d8342f2783f1aa770750dcadc0549ce873acaf5c98c9a5f5b70029

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000d79b3d20bf9e9d54c4da992fda0b52861fb6757500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000007d000000000000000000000000000000000000000000000000000000000000001f4

-----Decoded View---------------
Arg [0] : _pvpListener (address): 0xd79b3d20BF9e9d54C4da992FDa0b52861FB67575
Arg [1] : _pvpCut (uint256): 500
Arg [2] : _tournamentCut (uint256): 1500
Arg [3] : _incentiveCut (uint256): 500
Arg [4] : _tournamentOwnersCut (uint256): 2000
Arg [5] : _tournamentIncentiveCut (uint256): 500

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000d79b3d20bf9e9d54c4da992fda0b52861fb67575
Arg [1] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [2] : 00000000000000000000000000000000000000000000000000000000000005dc
Arg [3] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [4] : 00000000000000000000000000000000000000000000000000000000000007d0
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001f4


Swarm Source

bzzr://99dac81d836679d8342f2783f1aa770750dcadc0549ce873acaf5c98c9a5f5b7

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.