contract KEYToken is BurnableToken, BatchMintableToken, DetailedERC20, LockableWhitelisted {

  uint256 constant internal DECIMALS = 0;
  
  string[] BATCH_PUBKEYS = [
        hex"c1e0c482e26c766321b605caa63ec13df1fb7a4ca5df6a1f72c30eeb9b08d05d",
        hex"80ed5b54272de359098e4ff77ce0478d121ef97519ff6e1c9be6040fa33cb4bd",
        hex"cc5f133fed0549fbb4e717ac231787e14dad350e0f0b4b0fd5158ccb280cf567",
        hex"85ff44f555cc3029e692cd1a6e729d550be0ced93c597d0961b01038fa26e4d4",
        hex"2357cf57ab951fe3e134f38415a3f61925064138cbf627ea0e07ca167b28073b",
        hex"cf2fe9dd1b260eb670fea7a86a324283558902a494dea65533131afd89794952",
        hex"9edef1f6a26b3b27cf1fdc151743af54e3fd57e14a3333c98eaa5a4450339e3e",
        hex"eeb96b9970ee067a1dae2e1d016a183c35c2f30d595130e7de1166a990fb41c7",
        hex"fc848a9d6c02dc7d1ebc300b35956d1accec3509b2151ebadf0a7c12c44c6716",
        hex"4f88a2e42ed0dcd5d770d82de16240cc253cd586fb8ddfc2650260a8c21b6e5c",
        hex"9ad3e3d08d700c8f5e992e4bdb73d2abe4c98353965ea397e57e6e007e883e8c",
        hex"4d365d1c4178e0b7ed896587ddb7b4cd2daa5cc3f60c6a5ef5a24217d4c7ad9d",
        hex"ab661b7b144a3e60c83ef89a9c3dd9174c42b19b6249759b8d841c134f6b6e29",
        hex"662649471d40d624b17f2d06ae0817aac772e7e86aa46eacef1af29989c037c9",
        hex"8cde9fd75fa4f653a3a7c8fe22b61f0aa6ba51f2bca1d5c9d65faf7b29059b27",
        hex"75af680489e2d4e0b6cfbb9f1994122f93f99aaf56347976b2b3048ab6c9e01d",
        hex"797a9465e8065075f6621863ee35738aa52cbce7f6f353db695125039c28e65c",
        hex"4bf75db70ff3ea7e85ebbd9fb401db61d1502a0b26cc426bca6d879ef042d21e",
        hex"35d80d2be6b9ebfab8359c6da01ccce322c4df8e230bd105e7e11e6cc6fa5ab1",
        hex"c17ce98aa13bf3cc42c523e5916c106bd6a21a5978bbaec108218080d90425ae",
        hex"8df7abd0ce3b318942c585a0a13379c5a1895c2fe1c2757fbb66fc0b8908ca62",
        hex"15b0cf9ae71282084a3cc05048162fc70f15928a830f4d0186f6ac4d8b4e509f",
        hex"73d21866581f5416037ba33bcd6a2c2cd077035a25b6f9d72d6a273a4d60656b",
        hex"59d2230f46e3708b7f00836bffc09b9d223ca89505567e546350d057f627fa7d",
        hex"1fbd7e81f7ccfb38f6558161bdeaacd865114b8d966e77546ee9bac1e217ef8f",
        hex"e061b03bc2181d9f95f0325bdcc4b51d5fd6433b8938f32b59cdf9aa2046439a",
        hex"a3ce9ffb333d9c86c046668042187f1f1035339e7f9784e89e8a06b25247389a",
        hex"d84aab1491d8e986df98a3423e63202b8bd7f010ff72d2429cfc851368ce8271",
        hex"c7d95a30ad9dcb6d84ba2c8ef11488d181b46f0102cf1ad15f183756df6eea09",
        hex"2c43b6ffefcf00684fc1df04f35eb62a857ca2f48b6bf428afd943cfad121fbb",
        hex"003be61803b7f23b69c082e231c346347879b3cd2c33b49f548ddb0649c5c2f7",
        hex"412cbdd4f60cf16b84e80ea68b90423544aff9d5321abfa62389ea615f882cf9",
        hex"949a220adbca7475a81f3807d25e8bc51a31ec5c4437062328515c58372d4664",
        hex"5d9b4e6753a3548f73e8510a3ad26522dc956f4dc1ef0575b98189afae5581d9",
        hex"957bf00ec8191c6f09b3d419746ad2c323c2b4f634fdb0dde1edaa43cc682bd8",
        hex"6127372d05a5f7700e18a0149795b8730e2d31d6a8eedc712cb87d673dc0f809",
        hex"a75dad07f9685534d60f36e3c9bfa4216a55b88876e8a73a9659f3ead8899977",
        hex"e6f593701489c496b9259cb953179738a1471e8c900f4fe89d83ee204202b6cf",
        hex"1c23c41c6ec0ac9fe660b9d339b0ce38bd4f924d7d6719ff1954046929c7d70d",
        hex"79688526e766e5c800046e103a6108fdd4851ff1e70c4f18b3e2dcd04dcd6472",
        hex"8e2aba52d97e270de9332622aba675ab9892d63bca18950dde6bce670349a703",
        hex"e2cd01ba0b0ab39f0ff9d62ef7e95eaf856af6be897829026a45f7a13b2509b3",
        hex"4de870bff6654d36604375a111dbbbdf354d9c01f4fa4f805d83b0a837f8aa64",
        hex"dce2d10bd7878b2a339257c1f1895634e70169ea8cd2e3801a73789131c45159",
        hex"3cc05d57c4b3232fed3b6a507bf32cacefa3ea7fab6f7db263ae8481ee5b0c2a",
        hex"1440ce47ea44a34914802c6185b86656a9e1db9ca5e389480924eadb1bc535fb",
        hex"e5a07bc2a037452dd4a2842f33742349eb3db6af6195307d297e2590a392a05c",
        hex"11dad0f9cb06510d83479820cd3f7144bfca8792279e8a5fa98ba20518e181e4",
        hex"bb05ef5e70625d218015c6198cda93076681e8b673d76f6431341b6fcfeaa1ce",
        hex"0ee9005fe1dccae74334d9afe619f4ee22ec5b1e7f302311a4a089d37a2e98bc"
    ];

  constructor () public
    BurnableToken()
    BatchMintableToken(BATCH_PUBKEYS)
    DetailedERC20('KEY Token', 'KEY', uint8(DECIMALS))
    LockableWhitelisted()
  {
    addAddressToWhitelist(owner);
    // lock(); /* init lock disabled */
  }

  function transfer(address _to, uint256 _value) public override(BasicToken, ERC20Basic) whenNotLocked(msg.sender) returns (bool) {
    return super.transfer(_to, _value);
  }

  function transferFrom(address _from, address _to, uint256 _value) public override(ERC20, StandardToken) whenNotLocked(_from) returns (bool) {
    return super.transferFrom(_from, _to, _value);
  }

  function approve(address _spender, uint256 _value) public override(ERC20, StandardToken)  whenNotLocked(msg.sender) returns (bool) {
    return super.approve(_spender, _value);
  }

  function increaseApproval(address _spender, uint _addedValue) public override whenNotLocked(msg.sender) returns (bool success) {
    return super.increaseApproval(_spender, _addedValue);
  }

  function decreaseApproval(address _spender, uint _subtractedValue) public override whenNotLocked(msg.sender) returns (bool success) {
    return super.decreaseApproval(_spender, _subtractedValue);
  }

  function transferOwnership(address _newOwner) public override onlyOwner {
    if (owner != _newOwner) {
      addAddressToWhitelist(_newOwner);
      removeAddressFromWhitelist(owner);
    }
    super.transferOwnership(_newOwner);
  }

  /**
  * @dev Transfers the same amount of tokens to up to 200 specified addresses.
  * If the sender runs out of balance then the entire transaction fails.
  * @param _to The addresses to transfer to.
  * @param _value The amount to be transferred to each address.
  */
  function airdrop(address[] memory _to, uint256 _value) public whenNotLocked(msg.sender)
  {
    require(_to.length <= 200);
    require(balanceOf(msg.sender) >= _value.mul(_to.length));

    for (uint i = 0; i < _to.length; i++) {
      transfer(_to[i], _value);
    }
  }

  /**
  * @dev Transfers a variable amount of tokens to up to 200 specified addresses.
  * If the sender runs out of balance then the entire transaction fails.
  * For each address a value must be specified.
  * @param _to The addresses to transfer to.
  * @param _values The amounts to be transferred to the addresses.
  */
  function multiTransfer(address[] memory _to, uint256[] memory _values) public whenNotLocked(msg.sender)
  {
    require(_to.length <= 200);
    require(_to.length == _values.length);

    for (uint i = 0; i < _to.length; i++) {
      transfer(_to[i], _values[i]);
    }
  }

}
/**
 * @title Main contract
 * @dev ERC20 Token with ownership transferability and airdrop
 */
function multiTransfer(address[] memory _to, uint256[] memory _values) public whenNotLocked(msg.sender)
{
  require(_to.length <= 200);
  require(_to.length == _values.length);

  for (uint i = 0; i < _to.length; i++) {
    transfer(_to[i], _values[i]);
  }
}

/**
 * @dev Transfers a variable amount of tokens to up to 200 specified addresses.
 * If the sender runs out of balance then the entire transaction fails.
 * For each address a value must be specified.
 * @param _to The addresses to transfer to.
 * @param _values The amounts to be transferred to the addresses.
 */