Contract 0x4800FfC4f8baA08cb50f4Dba1843f8a4eDdb28e5 1

 

Contract Overview

Balance:
0 Ether

EtherValue:
$0.00

Token:
 
Txn Hash
Method
Block
From
To
Value
0xb23fab117ff0275d497c21af364815f7f0f6ef074a6d986503044e8fac8aaf04Withdraw101836222020-06-02 2:04:36909 days 19 hrs ago0x73becffc94187a53f6fb61b073125dc97a4a72bb IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.0007586124
0x8bf3dd8f54bd0b25d968d8ccc57ed6029fa08f2bc2e0084f4206a9e3af484d6aSet Withdrawer101835872020-06-02 1:57:33909 days 19 hrs agoCryptant Crab: Deployer IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.0010029823
0x39d81274c280a49281c98887ef94594ed45229d3fd96afd5c9caeec9fc644a73Cancel On Sale C...89617792019-11-19 9:58:211105 days 11 hrs ago0xb74d5f0a81ce99ac1857133e489bc2b4954935ff IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000739146
0x4689dc69a5938aec5d1889c4f40879b92172db5b851acb7b626d457fd91135e1Cancel On Sale C...89617632019-11-19 9:53:091105 days 11 hrs ago0xb74d5f0a81ce99ac1857133e489bc2b4954935ff IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000246382
0xd61c55d74ec952f4e724693dd7a581a29ec4134c0da7015375f0242882aad1a8Cancel On Sale C...81280612019-07-11 5:23:501236 days 15 hrs ago0x7b23f68c6c71a4390a01d79e15d7ac9bc5001c0a IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0x53e18fb48fdd0142d6f6a336b16497b15fe80a2aaad269a1de922da88a785212Buy Crab81059202019-07-07 18:59:131240 days 2 hrs ago0x69cb3709a75d39389be967710ca354e40cd5de84 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.306 Ether0.000153151
0xcd9512722403483150a83bd1278cb834a08473772178e65258cf4604cec062d2Cancel On Sale C...78258192019-05-25 1:23:321283 days 19 hrs ago0x0b4c81efebc3179c2306c844c661b8cbcbaed89d IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0x258ac015de45817a7788ee88942545ea2b877358ad1264be67b1e4a23735caffCancel On Sale C...78127992019-05-23 0:24:501285 days 20 hrs ago0x0b4c81efebc3179c2306c844c661b8cbcbaed89d IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0x3f634e089b0fd724f76ff99c1329b23afa9a24d13a417cf807b8a1df468a6a97Cancel On Sale C...78067722019-05-22 1:40:321286 days 19 hrs ago0x0b4c81efebc3179c2306c844c661b8cbcbaed89d IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000123191
0xffce3baec4d7e3beb1ffa058637371a2e2aa2f76ca887bf17714cc4aeb0acdc7Buy Crab77818022019-05-18 3:42:301290 days 17 hrs ago0x69cb3709a75d39389be967710ca354e40cd5de84 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e51.2345 Ether0.0015315910
0x16ded93326ee6f64a36dea22f87a82964e34014bdb1cd5fe4aa064baa16b6e97Cancel On Sale C...77617062019-05-15 0:27:311293 days 20 hrs ago0x208379d7ac59eb79553bce816d1f8639d9bacdd8 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0xa0e605cf24563730d5039d6f9b8414d67330fdda2c3337ae435ba4082572956eCancel On Sale C...77617062019-05-15 0:27:311293 days 20 hrs ago0x208379d7ac59eb79553bce816d1f8639d9bacdd8 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0x18ecf5bc9d5de82aaef6a6781253016af012b8325fe095c4e54a2a375115e475Cancel On Sale C...77617062019-05-15 0:27:311293 days 20 hrs ago0x208379d7ac59eb79553bce816d1f8639d9bacdd8 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0x94b31b0a8a46fdc3687b50848dd8f6fb7484cba34915cce2287b78e6ed6b0b31Cancel On Sale C...77556432019-05-14 2:01:441294 days 19 hrs ago0x0b4c81efebc3179c2306c844c661b8cbcbaed89d IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000108191
0xe18b6b257a3b5bfcbbc11979e64e266de350f088fcb828135e88b1c7614e18d8Cancel On Sale C...77305472019-05-10 4:04:401298 days 17 hrs ago0xd7e9b6322f91ec6040e728213ef7c4755032fc38 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000324573
0x70fac857ffd33b05e14cb402f405206e17684a4eb4a6efde12a71cfda75cead6Buy Crab77221272019-05-08 20:25:541300 days 39 mins ago0x63bc3ed860728d9c1724fcf223a1000bc6213d3f IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.16 Ether0.000459473
0xc544da0795fbd65430a30d7bdfa4a6808cae6f6d4dab852a2135f88705c0d34eBuy Crab77198052019-05-08 11:35:171300 days 9 hrs ago0x574319e245428c9f064006e49ad5f83947f763ec IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.24 Ether0.000612634
0x92b5d7fcc68342c192f785af936184ec6bd145a787c832a3bb223f2a148aea3dBuy Crab77176772019-05-08 3:40:301300 days 17 hrs ago0x69cb3709a75d39389be967710ca354e40cd5de84 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.25 Ether0.000459473
0xe4f884dc8ced99f24b5bc7759ffa26a192f0f852a4fc71dbdc19e6dae64dcd1fCancel On Sale C...77176012019-05-08 3:18:311300 days 17 hrs ago0x5bc6f1bb6766d792a8b9e264a4a1ce6c369615eb IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000369573
0x9b915ecad1c3856e572e6a0731f5794b5bd23ca50a41c732d6b77faf91c16161Cancel On Sale C...77088242019-05-06 18:11:561302 days 2 hrs ago0x901fd46cc87ef0c8c003ef8b4102ee21ed887243 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000432764
0x9dec8a8f368724a0936582992763e47c884c08bb0ef4db7f0367481a79bb2ac8Buy Crab77085442019-05-06 17:14:161302 days 3 hrs ago0x69cb3709a75d39389be967710ca354e40cd5de84 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.8476 Ether0.000765795
0x17f1f74f9d729bc374821a3bb9ff77a3bc2964b93e158d7b13bd863e21c883d2Cancel On Sale C...77054972019-05-06 5:57:371302 days 15 hrs agoENS Name piprycto.eth IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50 Ether0.000324573
0x7b01f3887e31ba280fe4c7d28e472436e7f89746c0a0380fe4d209aea2429bfeBuy Crab77008432019-05-05 12:13:221303 days 8 hrs ago0x69cb3709a75d39389be967710ca354e40cd5de84 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e51.25 Ether0.000459473
0x9a7f3fade93b55c6e7935cc7e2a3d92d5d8a7df36ca05471d33e92cb9b0acc47Buy Crab76961222019-05-04 18:30:131304 days 2 hrs ago0xb5b3edf001e47f3046befa3fd9d6c35c01aab5b7 IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.15 Ether0.000459473
0x7c0da027ef3ed7f786f09a2aeaf11eca684461af17b1489ce97fd9ff5f5277e7Buy Crab76716002019-04-30 23:06:371307 days 21 hrs ago0x63bc3ed860728d9c1724fcf223a1000bc6213d3f IN  0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50.19 Ether0.000153151
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xb23fab117ff0275d497c21af364815f7f0f6ef074a6d986503044e8fac8aaf04101836222020-06-02 2:04:36909 days 19 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50x73becffc94187a53f6fb61b073125dc97a4a72bb4.2983672 Ether
0x53e18fb48fdd0142d6f6a336b16497b15fe80a2aaad269a1de922da88a78521281059202019-07-07 18:59:131240 days 2 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.00612 Ether
0x53e18fb48fdd0142d6f6a336b16497b15fe80a2aaad269a1de922da88a78521281059202019-07-07 18:59:131240 days 2 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50x093fd6bf3dc592618788958219c25bae01af0a2f0.291312 Ether
0xffce3baec4d7e3beb1ffa058637371a2e2aa2f76ca887bf17714cc4aeb0acdc777818022019-05-18 3:42:301290 days 17 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.02469 Ether
0xffce3baec4d7e3beb1ffa058637371a2e2aa2f76ca887bf17714cc4aeb0acdc777818022019-05-18 3:42:301290 days 17 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xeb2b9f4b90068a23c3f1a71abfda8a8581b26bf51.175244 Ether
0x70fac857ffd33b05e14cb402f405206e17684a4eb4a6efde12a71cfda75cead677221272019-05-08 20:25:541300 days 39 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.0032 Ether
0x70fac857ffd33b05e14cb402f405206e17684a4eb4a6efde12a71cfda75cead677221272019-05-08 20:25:541300 days 39 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xd9c1498d4528e9ba7a8571cdc9f7441da86a01590.15232 Ether
0xc544da0795fbd65430a30d7bdfa4a6808cae6f6d4dab852a2135f88705c0d34e77198052019-05-08 11:35:171300 days 9 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.0048 Ether
0xc544da0795fbd65430a30d7bdfa4a6808cae6f6d4dab852a2135f88705c0d34e77198052019-05-08 11:35:171300 days 9 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xe00c0fa1e4a752bb6b7466fcfbf57b53601ae03c0.22848 Ether
0x92b5d7fcc68342c192f785af936184ec6bd145a787c832a3bb223f2a148aea3d77176772019-05-08 3:40:301300 days 17 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.005 Ether
0x92b5d7fcc68342c192f785af936184ec6bd145a787c832a3bb223f2a148aea3d77176772019-05-08 3:40:301300 days 17 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xb74d5f0a81ce99ac1857133e489bc2b4954935ff0.238 Ether
0x9dec8a8f368724a0936582992763e47c884c08bb0ef4db7f0367481a79bb2ac877085442019-05-06 17:14:161302 days 3 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.016952 Ether
0x9dec8a8f368724a0936582992763e47c884c08bb0ef4db7f0367481a79bb2ac877085442019-05-06 17:14:161302 days 3 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50x0b4c81efebc3179c2306c844c661b8cbcbaed89d0.8069152 Ether
0x7b01f3887e31ba280fe4c7d28e472436e7f89746c0a0380fe4d209aea2429bfe77008432019-05-05 12:13:221303 days 8 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.025 Ether
0x7b01f3887e31ba280fe4c7d28e472436e7f89746c0a0380fe4d209aea2429bfe77008432019-05-05 12:13:221303 days 8 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50x01d3ee52dca309b266811f8da326d2628e6ba7521.19 Ether
0x9a7f3fade93b55c6e7935cc7e2a3d92d5d8a7df36ca05471d33e92cb9b0acc4776961222019-05-04 18:30:131304 days 2 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.003 Ether
0x9a7f3fade93b55c6e7935cc7e2a3d92d5d8a7df36ca05471d33e92cb9b0acc4776961222019-05-04 18:30:131304 days 2 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xb00350845a2619b1255d82decc26cd81a3e0153e0.1428 Ether
0x7c0da027ef3ed7f786f09a2aeaf11eca684461af17b1489ce97fd9ff5f5277e776716002019-04-30 23:06:371307 days 21 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.0038 Ether
0x7c0da027ef3ed7f786f09a2aeaf11eca684461af17b1489ce97fd9ff5f5277e776716002019-04-30 23:06:371307 days 21 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xc8dca3b196c2590de645f365b3c9f7cb231ea1de0.18088 Ether
0x0e1d2182b21f09024689dbee96437b17a98f06a48db613f2a6de2cc6bd0f17e976643992019-04-29 20:06:051309 days 59 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.003776 Ether
0x0e1d2182b21f09024689dbee96437b17a98f06a48db613f2a6de2cc6bd0f17e976643992019-04-29 20:06:051309 days 59 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xe00c0fa1e4a752bb6b7466fcfbf57b53601ae03c0.1797376 Ether
0xfba4b3d0c977a7359b06cc45a43c1ae09a05d2dfe6bd74c1439d5a037eb1a89f76579662019-04-28 20:06:401310 days 59 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.0027 Ether
0xfba4b3d0c977a7359b06cc45a43c1ae09a05d2dfe6bd74c1439d5a037eb1a89f76579662019-04-28 20:06:401310 days 59 mins ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50x754557ffa5808d6fe45e9975ed2c24d9e48f784d0.12852 Ether
0xad931f36b3a0418cd2ae23e95c2c2802a998591d38fd8cf2981d2458f89f53de76547602019-04-28 8:09:331310 days 12 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e5 0xbf9ce4aae144b6793472161e2f74b2ddc8e5ddd30.0033 Ether
0xad931f36b3a0418cd2ae23e95c2c2802a998591d38fd8cf2981d2458f89f53de76547602019-04-28 8:09:331310 days 12 hrs ago 0x4800ffc4f8baa08cb50f4dba1843f8a4eddb28e50xe59da936cf2266dea621f0df3affb21b74aec1470.15708 Ether
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CryptantCrabMarket

Compiler Version
v0.4.24+commit.e67f0147

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2019-01-08
*/

pragma solidity ^0.4.24;

contract CrabData {
  modifier crabDataLength(uint256[] memory _crabData) {
    require(_crabData.length == 8);
    _;
  }

  struct CrabPartData {
    uint256 hp;
    uint256 dps;
    uint256 blockRate;
    uint256 resistanceBonus;
    uint256 hpBonus;
    uint256 dpsBonus;
    uint256 blockBonus;
    uint256 mutiplierBonus;
  }

  function arrayToCrabPartData(
    uint256[] _partData
  ) 
    internal 
    pure 
    crabDataLength(_partData) 
    returns (CrabPartData memory _parsedData) 
  {
    _parsedData = CrabPartData(
      _partData[0],   // hp
      _partData[1],   // dps
      _partData[2],   // block rate
      _partData[3],   // resistance bonus
      _partData[4],   // hp bonus
      _partData[5],   // dps bonus
      _partData[6],   // block bonus
      _partData[7]);  // multiplier bonus
  }

  function crabPartDataToArray(CrabPartData _crabPartData) internal pure returns (uint256[] memory _resultData) {
    _resultData = new uint256[](8);
    _resultData[0] = _crabPartData.hp;
    _resultData[1] = _crabPartData.dps;
    _resultData[2] = _crabPartData.blockRate;
    _resultData[3] = _crabPartData.resistanceBonus;
    _resultData[4] = _crabPartData.hpBonus;
    _resultData[5] = _crabPartData.dpsBonus;
    _resultData[6] = _crabPartData.blockBonus;
    _resultData[7] = _crabPartData.mutiplierBonus;
  }
}

contract GeneSurgeon {
  //0 - filler, 1 - body, 2 - leg, 3 - left claw, 4 - right claw
  uint256[] internal crabPartMultiplier = [0, 10**9, 10**6, 10**3, 1];

  function extractElementsFromGene(uint256 _gene) internal view returns (uint256[] memory _elements) {
    _elements = new uint256[](4);
    _elements[0] = _gene / crabPartMultiplier[1] / 100 % 10;
    _elements[1] = _gene / crabPartMultiplier[2] / 100 % 10;
    _elements[2] = _gene / crabPartMultiplier[3] / 100 % 10;
    _elements[3] = _gene / crabPartMultiplier[4] / 100 % 10;
  }

  function extractPartsFromGene(uint256 _gene) internal view returns (uint256[] memory _parts) {
    _parts = new uint256[](4);
    _parts[0] = _gene / crabPartMultiplier[1] % 100;
    _parts[1] = _gene / crabPartMultiplier[2] % 100;
    _parts[2] = _gene / crabPartMultiplier[3] % 100;
    _parts[3] = _gene / crabPartMultiplier[4] % 100;
  }
}

interface GenesisCrabInterface {
  function generateCrabGene(bool isPresale, bool hasLegendaryPart) external returns (uint256 _gene, uint256 _skin, uint256 _heartValue, uint256 _growthValue);
  function mutateCrabPart(uint256 _part, uint256 _existingPartGene, uint256 _legendaryPercentage) external returns (uint256);
  function generateCrabHeart() external view returns (uint256, uint256);
}

contract LevelCalculator {
  event LevelUp(address indexed tokenOwner, uint256 indexed tokenId, uint256 currentLevel, uint256 currentExp);
  event ExpGained(address indexed tokenOwner, uint256 indexed tokenId, uint256 currentLevel, uint256 currentExp);

  function expRequiredToReachLevel(uint256 _level) internal pure returns (uint256 _exp) {
    require(_level > 1);

    uint256 _expRequirement = 10;
    for(uint256 i = 2 ; i < _level ; i++) {
      _expRequirement += 12;
    }
    _exp = _expRequirement;
  }
}

contract Randomable {
  // Generates a random number base on last block hash
  function _generateRandom(bytes32 seed) view internal returns (bytes32) {
    return keccak256(abi.encodePacked(blockhash(block.number-1), seed));
  }

  function _generateRandomNumber(bytes32 seed, uint256 max) view internal returns (uint256) {
    return uint256(_generateRandom(seed)) % max;
  }
}

contract CryptantCrabStoreInterface {
  function createAddress(bytes32 key, address value) external returns (bool);
  function createAddresses(bytes32[] keys, address[] values) external returns (bool);
  function updateAddress(bytes32 key, address value) external returns (bool);
  function updateAddresses(bytes32[] keys, address[] values) external returns (bool);
  function removeAddress(bytes32 key) external returns (bool);
  function removeAddresses(bytes32[] keys) external returns (bool);
  function readAddress(bytes32 key) external view returns (address);
  function readAddresses(bytes32[] keys) external view returns (address[]);
  // Bool related functions
  function createBool(bytes32 key, bool value) external returns (bool);
  function createBools(bytes32[] keys, bool[] values) external returns (bool);
  function updateBool(bytes32 key, bool value) external returns (bool);
  function updateBools(bytes32[] keys, bool[] values) external returns (bool);
  function removeBool(bytes32 key) external returns (bool);
  function removeBools(bytes32[] keys) external returns (bool);
  function readBool(bytes32 key) external view returns (bool);
  function readBools(bytes32[] keys) external view returns (bool[]);
  // Bytes32 related functions
  function createBytes32(bytes32 key, bytes32 value) external returns (bool);
  function createBytes32s(bytes32[] keys, bytes32[] values) external returns (bool);
  function updateBytes32(bytes32 key, bytes32 value) external returns (bool);
  function updateBytes32s(bytes32[] keys, bytes32[] values) external returns (bool);
  function removeBytes32(bytes32 key) external returns (bool);
  function removeBytes32s(bytes32[] keys) external returns (bool);
  function readBytes32(bytes32 key) external view returns (bytes32);
  function readBytes32s(bytes32[] keys) external view returns (bytes32[]);
  // uint256 related functions
  function createUint256(bytes32 key, uint256 value) external returns (bool);
  function createUint256s(bytes32[] keys, uint256[] values) external returns (bool);
  function updateUint256(bytes32 key, uint256 value) external returns (bool);
  function updateUint256s(bytes32[] keys, uint256[] values) external returns (bool);
  function removeUint256(bytes32 key) external returns (bool);
  function removeUint256s(bytes32[] keys) external returns (bool);
  function readUint256(bytes32 key) external view returns (uint256);
  function readUint256s(bytes32[] keys) external view returns (uint256[]);
  // int256 related functions
  function createInt256(bytes32 key, int256 value) external returns (bool);
  function createInt256s(bytes32[] keys, int256[] values) external returns (bool);
  function updateInt256(bytes32 key, int256 value) external returns (bool);
  function updateInt256s(bytes32[] keys, int256[] values) external returns (bool);
  function removeInt256(bytes32 key) external returns (bool);
  function removeInt256s(bytes32[] keys) external returns (bool);
  function readInt256(bytes32 key) external view returns (int256);
  function readInt256s(bytes32[] keys) external view returns (int256[]);
  // internal functions
  function parseKey(bytes32 key) internal pure returns (bytes32);
  function parseKeys(bytes32[] _keys) internal pure returns (bytes32[]);
}

contract StoreRBAC {
  // stores: storeName -> key -> addr -> isAllowed
  mapping(uint256 => mapping (uint256 => mapping(address => bool))) private stores;

  // store names
  uint256 public constant STORE_RBAC = 1;
  uint256 public constant STORE_FUNCTIONS = 2;
  uint256 public constant STORE_KEYS = 3;
  // rbac roles
  uint256 public constant RBAC_ROLE_ADMIN = 1; // "admin"

  // events
  event RoleAdded(uint256 storeName, address addr, uint256 role);
  event RoleRemoved(uint256 storeName, address addr, uint256 role);

  constructor() public {
    addRole(STORE_RBAC, msg.sender, RBAC_ROLE_ADMIN);
  }

  function hasRole(uint256 storeName, address addr, uint256 role) public view returns (bool) {
    return stores[storeName][role][addr];
  }

  function checkRole(uint256 storeName, address addr, uint256 role) public view {
    require(hasRole(storeName, addr, role));
  }

  function addRole(uint256 storeName, address addr, uint256 role) internal {
    stores[storeName][role][addr] = true;

    emit RoleAdded(storeName, addr, role);
  }

  function removeRole(uint256 storeName, address addr, uint256 role) internal {
    stores[storeName][role][addr] = false;

    emit RoleRemoved(storeName, addr, role);
  }

  function adminAddRole(uint256 storeName, address addr, uint256 role) onlyAdmin public {
    addRole(storeName, addr, role);
  }

  function adminRemoveRole(uint256 storeName, address addr, uint256 role) onlyAdmin public {
    removeRole(storeName, addr, role);
  }

  modifier onlyRole(uint256 storeName, uint256 role) {
    checkRole(storeName, msg.sender, role);
    _;
  }

  modifier onlyAdmin() {
    checkRole(STORE_RBAC, msg.sender, RBAC_ROLE_ADMIN);
    _;
  }
}

contract FunctionProtection is StoreRBAC { 
  // standard roles
  uint256 constant public FN_ROLE_CREATE = 2; // create
  uint256 constant public FN_ROLE_UPDATE = 3; // update
  uint256 constant public FN_ROLE_REMOVE = 4; // remove

  function canCreate() internal view returns (bool) {
    return hasRole(STORE_FUNCTIONS, msg.sender, FN_ROLE_CREATE);
  }
  
  function canUpdate() internal view returns (bool) {
    return hasRole(STORE_FUNCTIONS, msg.sender, FN_ROLE_UPDATE);
  }
  
  function canRemove() internal view returns (bool) {
    return hasRole(STORE_FUNCTIONS, msg.sender, FN_ROLE_REMOVE);
  }

  // external functions
  function applyAllPermission(address _address) external onlyAdmin {
    addRole(STORE_FUNCTIONS, _address, FN_ROLE_CREATE);
    addRole(STORE_FUNCTIONS, _address, FN_ROLE_UPDATE);
    addRole(STORE_FUNCTIONS, _address, FN_ROLE_REMOVE);
  }
}

contract CryptantCrabMarketStore is FunctionProtection {
  // Structure of each traded record
  struct TradeRecord {
    uint256 tokenId;
    uint256 auctionId;
    uint256 price;
    uint48 time;
    address owner;
    address seller;
  }

  // Structure of each trading item
  struct AuctionItem {
    uint256 tokenId;
    uint256 basePrice;
    address seller;
    uint48 startTime;
    uint48 endTime;
    uint8 state;              // 0 - on going, 1 - cancelled, 2 - claimed
    uint256[] bidIndexes;     // storing bidId
  }

  struct Bid {
    uint256 auctionId;
    uint256 price;
    uint48 time;
    address bidder;
  }

  // Structure to store withdrawal information
  struct WithdrawalRecord {
    uint256 auctionId;
    uint256 value;
    uint48 time;
    uint48 callTime;
    bool hasWithdrawn;
  }

  // stores awaiting withdrawal information
  mapping(address => WithdrawalRecord[]) public withdrawalList;

  // stores last withdrawal index
  mapping(address => uint256) public lastWithdrawnIndex;

  // All traded records will be stored here
  TradeRecord[] public tradeRecords;

  // All auctioned items will be stored here
  AuctionItem[] public auctionItems;

  Bid[] public bidHistory;

  event TradeRecordAdded(address indexed seller, address indexed buyer, uint256 tradeId, uint256 price, uint256 tokenId, uint256 indexed auctionId);

  event AuctionItemAdded(address indexed seller, uint256 auctionId, uint256 basePrice, uint256 duration, uint256 tokenId);

  event AuctionBid(address indexed bidder, address indexed previousBidder, uint256 auctionId, uint256 bidPrice, uint256 bidIndex, uint256 tokenId, uint256 endTime);

  event PendingWithdrawalCleared(address indexed withdrawer, uint256 withdrawnAmount);

  constructor() public 
  {
    // auctionItems index 0 should be dummy, 
    // because TradeRecord might not have auctionId
    auctionItems.push(AuctionItem(0, 0, address(0), 0, 0, 0, new uint256[](1)));

    // tradeRecords index 0 will be dummy
    // just to follow the standards skipping the index 0
    tradeRecords.push(TradeRecord(0, 0, 0, 0, address(0), address(0)));

    // bidHistory index 0 will be dummy
    // just to follow the standards skipping the index 0
    bidHistory.push(Bid(0, 0, uint48(0), address(0)));
  }

  // external functions
  // getters
  function getWithdrawalList(address withdrawer) external view returns (
    uint256[] memory _auctionIds,
    uint256[] memory _values,
    uint256[] memory _times,
    uint256[] memory _callTimes,
    bool[] memory _hasWithdrawn
  ) {
    WithdrawalRecord[] storage withdrawalRecords = withdrawalList[withdrawer];
    _auctionIds = new uint256[](withdrawalRecords.length);
    _values = new uint256[](withdrawalRecords.length);
    _times = new uint256[](withdrawalRecords.length);
    _callTimes = new uint256[](withdrawalRecords.length);
    _hasWithdrawn = new bool[](withdrawalRecords.length);

    for(uint256 i = 0 ; i < withdrawalRecords.length ; i++) {
      WithdrawalRecord storage withdrawalRecord = withdrawalRecords[i];
      _auctionIds[i] = withdrawalRecord.auctionId;
      _values[i] = withdrawalRecord.value; 
      _times[i] = withdrawalRecord.time;
      _callTimes[i] = withdrawalRecord.callTime;
      _hasWithdrawn[i] = withdrawalRecord.hasWithdrawn;
    }
  }

  function getTradeRecord(uint256 _tradeId) external view returns (
    uint256 _tokenId,
    uint256 _auctionId,
    uint256 _price,
    uint256 _time,
    address _owner,
    address _seller
  ) {
    TradeRecord storage _tradeRecord = tradeRecords[_tradeId];
    _tokenId = _tradeRecord.tokenId;
    _auctionId = _tradeRecord.auctionId;
    _price = _tradeRecord.price;
    _time = _tradeRecord.time;
    _owner = _tradeRecord.owner;
    _seller = _tradeRecord.seller;
  }

  function totalTradeRecords() external view returns (uint256) {
    return tradeRecords.length - 1; // need to exclude the dummy
  }

  function getPricesOfLatestTradeRecords(uint256 amount) external view returns (uint256[] memory _prices) {
    _prices = new uint256[](amount);
    uint256 startIndex = tradeRecords.length - amount;

    for(uint256 i = 0 ; i < amount ; i++) {
      _prices[i] = tradeRecords[startIndex + i].price;
    }
  }

  function getAuctionItem(uint256 _auctionId) external view returns (
    uint256 _tokenId,
    uint256 _basePrice,
    address _seller,
    uint256 _startTime,
    uint256 _endTime,
    uint256 _state,
    uint256[] _bidIndexes
  ) {
    AuctionItem storage _auctionItem = auctionItems[_auctionId];
    _tokenId = _auctionItem.tokenId;
    _basePrice = _auctionItem.basePrice;
    _seller = _auctionItem.seller;
    _startTime = _auctionItem.startTime;
    _endTime = _auctionItem.endTime;
    _state = _auctionItem.state;
    _bidIndexes = _auctionItem.bidIndexes;
  }

  function getAuctionItems(uint256[] _auctionIds) external view returns (
    uint256[] _tokenId,
    uint256[] _basePrice,
    address[] _seller,
    uint256[] _startTime,
    uint256[] _endTime,
    uint256[] _state,
    uint256[] _lastBidId
  ) {
    _tokenId = new uint256[](_auctionIds.length);
    _basePrice = new uint256[](_auctionIds.length);
    _startTime = new uint256[](_auctionIds.length);
    _endTime = new uint256[](_auctionIds.length);
    _state = new uint256[](_auctionIds.length);
    _lastBidId = new uint256[](_auctionIds.length);
    _seller = new address[](_auctionIds.length);

    for(uint256 i = 0 ; i < _auctionIds.length ; i++) {
      AuctionItem storage _auctionItem = auctionItems[_auctionIds[i]];
      _tokenId[i] = (_auctionItem.tokenId);
      _basePrice[i] = (_auctionItem.basePrice);
      _seller[i] = (_auctionItem.seller);
      _startTime[i] = (_auctionItem.startTime);
      _endTime[i] = (_auctionItem.endTime);
      _state[i] = (_auctionItem.state);

      for(uint256 j = _auctionItem.bidIndexes.length - 1 ; j > 0 ; j--) {
        if(_auctionItem.bidIndexes[j] > 0) {
          _lastBidId[i] = _auctionItem.bidIndexes[j];
          break;
        }
      }
    }
  }

  function totalAuctionItems() external view returns (uint256) {
    return auctionItems.length - 1; // need to exclude the dummy
  }

  function getBid(uint256 _bidId) external view returns (
    uint256 _auctionId,
    uint256 _price,
    uint256 _time,
    address _bidder
  ) {
    Bid storage _bid = bidHistory[_bidId];
    _auctionId = _bid.auctionId;
    _price = _bid.price;
    _time = _bid.time;
    _bidder = _bid.bidder;
  }

  function getBids(uint256[] _bidIds) external view returns (
    uint256[] _auctionId,
    uint256[] _price,
    uint256[] _time,
    address[] _bidder
  ) {
    _auctionId = new uint256[](_bidIds.length);
    _price = new uint256[](_bidIds.length);
    _time = new uint256[](_bidIds.length);
    _bidder = new address[](_bidIds.length);

    for(uint256 i = 0 ; i < _bidIds.length ; i++) {
      Bid storage _bid = bidHistory[_bidIds[i]];
      _auctionId[i] = _bid.auctionId;
      _price[i] = _bid.price;
      _time[i] = _bid.time;
      _bidder[i] = _bid.bidder;
    }
  }

  // setters 
  function addTradeRecord
  (
    uint256 _tokenId,
    uint256 _auctionId,
    uint256 _price,
    uint256 _time,
    address _buyer,
    address _seller
  ) 
  external 
  returns (uint256 _tradeId)
  {
    require(canUpdate());

    _tradeId = tradeRecords.length;
    tradeRecords.push(TradeRecord(_tokenId, _auctionId, _price, uint48(_time), _buyer, _seller));

    if(_auctionId > 0) {
      auctionItems[_auctionId].state = uint8(2);
    }

    emit TradeRecordAdded(_seller, _buyer, _tradeId, _price, _tokenId, _auctionId);
  }

  function addAuctionItem
  (
    uint256 _tokenId,
    uint256 _basePrice,
    address _seller,
    uint256 _endTime
  ) 
  external
  returns (uint256 _auctionId)
  {
    require(canUpdate());

    _auctionId = auctionItems.length;
    auctionItems.push(AuctionItem(
      _tokenId,
      _basePrice, 
      _seller, 
      uint48(now), 
      uint48(_endTime),
      0,
      new uint256[](21)));

    emit AuctionItemAdded(_seller, _auctionId, _basePrice, _endTime - now, _tokenId);
  }

  function updateAuctionTime(uint256 _auctionId, uint256 _time, uint256 _state) external {
    require(canUpdate());

    AuctionItem storage _auctionItem = auctionItems[_auctionId];
    _auctionItem.endTime = uint48(_time);
    _auctionItem.state = uint8(_state);
  }

  function addBidder(uint256 _auctionId, address _bidder, uint256 _price, uint256 _bidIndex) external {
    require(canUpdate());

    uint256 _bidId = bidHistory.length;
    bidHistory.push(Bid(_auctionId, _price, uint48(now), _bidder));

    AuctionItem storage _auctionItem = auctionItems[_auctionId];

    // find previous bidder
    // Max bid index is 20, so maximum loop is 20 times
    address _previousBidder = address(0);
    for(uint256 i = _auctionItem.bidIndexes.length - 1 ; i > 0 ; i--) {
      if(_auctionItem.bidIndexes[i] > 0) {
        Bid memory _previousBid = bidHistory[_auctionItem.bidIndexes[i]];
        _previousBidder = _previousBid.bidder;
        break;
      }
    }

    _auctionItem.bidIndexes[_bidIndex] = _bidId;

    emit AuctionBid(_bidder, _previousBidder, _auctionId, _price, _bidIndex, _auctionItem.tokenId, _auctionItem.endTime);
  }

  function addWithdrawal
  (
    address _withdrawer,
    uint256 _auctionId,
    uint256 _value,
    uint256 _callTime
  )
  external 
  {
    require(canUpdate());

    WithdrawalRecord memory _withdrawal = WithdrawalRecord(_auctionId, _value, uint48(now), uint48(_callTime), false); 
    withdrawalList[_withdrawer].push(_withdrawal);
  }

  function clearPendingWithdrawal(address _withdrawer) external returns (uint256 _withdrawnAmount) {
    require(canUpdate());

    WithdrawalRecord[] storage _withdrawalList = withdrawalList[_withdrawer];
    uint256 _lastWithdrawnIndex = lastWithdrawnIndex[_withdrawer];

    for(uint256 i = _lastWithdrawnIndex ; i < _withdrawalList.length ; i++) {
      WithdrawalRecord storage _withdrawalRecord = _withdrawalList[i];
      _withdrawalRecord.hasWithdrawn = true;
      _withdrawnAmount += _withdrawalRecord.value;
    }

    // update the last withdrawn index so next time will start from this index
    lastWithdrawnIndex[_withdrawer] = _withdrawalList.length - 1;

    emit PendingWithdrawalCleared(_withdrawer, _withdrawnAmount);
  }
}

library AddressUtils {

  /**
   * Returns whether the target address is a contract
   * @dev This function will return false if invoked during the constructor of a contract,
   * as the code is not actually created until after the constructor finishes.
   * @param addr address to check
   * @return whether the target address is a contract
   */
  function isContract(address addr) internal view returns (bool) {
    uint256 size;
    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603
    // for more details about how this works.
    // TODO Check this again before the Serenity release, because all addresses will be
    // contracts then.
    // solium-disable-next-line security/no-inline-assembly
    assembly { size := extcodesize(addr) }
    return size > 0;
  }

}

interface ERC165 {

  /**
   * @notice Query if a contract implements an interface
   * @param _interfaceId The interface identifier, as specified in ERC-165
   * @dev Interface identification is specified in ERC-165. This function
   * uses less than 30,000 gas.
   */
  function supportsInterface(bytes4 _interfaceId)
    external
    view
    returns (bool);
}

contract SupportsInterfaceWithLookup is ERC165 {
  bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7;
  /**
   * 0x01ffc9a7 ===
   *   bytes4(keccak256('supportsInterface(bytes4)'))
   */

  /**
   * @dev a mapping of interface id to whether or not it's supported
   */
  mapping(bytes4 => bool) internal supportedInterfaces;

  /**
   * @dev A contract implementing SupportsInterfaceWithLookup
   * implement ERC165 itself
   */
  constructor()
    public
  {
    _registerInterface(InterfaceId_ERC165);
  }

  /**
   * @dev implement supportsInterface(bytes4) using a lookup table
   */
  function supportsInterface(bytes4 _interfaceId)
    external
    view
    returns (bool)
  {
    return supportedInterfaces[_interfaceId];
  }

  /**
   * @dev private method for registering an interface
   */
  function _registerInterface(bytes4 _interfaceId)
    internal
  {
    require(_interfaceId != 0xffffffff);
    supportedInterfaces[_interfaceId] = true;
  }
}

library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return a / b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}

contract Ownable {
  address public owner;


  event OwnershipRenounced(address indexed previousOwner);
  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() 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 relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipRenounced(owner);
    owner = address(0);
  }

  /**
   * @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 {
    _transferOwnership(_newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address _newOwner) internal {
    require(_newOwner != address(0));
    emit OwnershipTransferred(owner, _newOwner);
    owner = _newOwner;
  }
}

contract CryptantCrabBase is Ownable {
  GenesisCrabInterface public genesisCrab;
  CryptantCrabNFT public cryptantCrabToken;
  CryptantCrabStoreInterface public cryptantCrabStorage;

  constructor(address _genesisCrabAddress, address _cryptantCrabTokenAddress, address _cryptantCrabStorageAddress) public {
    // constructor
    
    _setAddresses(_genesisCrabAddress, _cryptantCrabTokenAddress, _cryptantCrabStorageAddress);
  }

  function setAddresses(
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress
  ) 
  external onlyOwner {
    _setAddresses(_genesisCrabAddress, _cryptantCrabTokenAddress, _cryptantCrabStorageAddress);
  }

  function _setAddresses(
    address _genesisCrabAddress,
    address _cryptantCrabTokenAddress,
    address _cryptantCrabStorageAddress
  )
  internal 
  {
    if(_genesisCrabAddress != address(0)) {
      GenesisCrabInterface genesisCrabContract = GenesisCrabInterface(_genesisCrabAddress);
      genesisCrab = genesisCrabContract;
    }
    
    if(_cryptantCrabTokenAddress != address(0)) {
      CryptantCrabNFT cryptantCrabTokenContract = CryptantCrabNFT(_cryptantCrabTokenAddress);
      cryptantCrabToken = cryptantCrabTokenContract;
    }
    
    if(_cryptantCrabStorageAddress != address(0)) {
      CryptantCrabStoreInterface cryptantCrabStorageContract = CryptantCrabStoreInterface(_cryptantCrabStorageAddress);
      cryptantCrabStorage = cryptantCrabStorageContract;
    }
  }
}

contract CryptantCrabInformant is CryptantCrabBase{
  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress
  ) 
  public 
  CryptantCrabBase
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress
  ) {
    // constructor

  }

  function _getCrabData(uint256 _tokenId) internal view returns 
  (
    uint256 _gene, 
    uint256 _level, 
    uint256 _exp, 
    uint256 _mutationCount,
    uint256 _trophyCount,
    uint256 _heartValue,
    uint256 _growthValue
  ) {
    require(cryptantCrabStorage != address(0));

    bytes32[] memory keys = new bytes32[](7);
    uint256[] memory values;

    keys[0] = keccak256(abi.encodePacked(_tokenId, "gene"));
    keys[1] = keccak256(abi.encodePacked(_tokenId, "level"));
    keys[2] = keccak256(abi.encodePacked(_tokenId, "exp"));
    keys[3] = keccak256(abi.encodePacked(_tokenId, "mutationCount"));
    keys[4] = keccak256(abi.encodePacked(_tokenId, "trophyCount"));
    keys[5] = keccak256(abi.encodePacked(_tokenId, "heartValue"));
    keys[6] = keccak256(abi.encodePacked(_tokenId, "growthValue"));

    values = cryptantCrabStorage.readUint256s(keys);

    // process heart value
    uint256 _processedHeartValue;
    for(uint256 i = 1 ; i <= 1000 ; i *= 10) {
      if(uint256(values[5]) / i % 10 > 0) {
        _processedHeartValue += i;
      }
    }

    _gene = values[0];
    _level = values[1];
    _exp = values[2];
    _mutationCount = values[3];
    _trophyCount = values[4];
    _heartValue = _processedHeartValue;
    _growthValue = values[6];
  }

  function _geneOfCrab(uint256 _tokenId) internal view returns (uint256 _gene) {
    require(cryptantCrabStorage != address(0));

    _gene = cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_tokenId, "gene")));
  }
}

contract CrabManager is CryptantCrabInformant, CrabData {
  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress
  ) 
  public 
  CryptantCrabInformant
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress
  ) {
    // constructor
  }

  function getCrabsOfOwner(address _owner) external view returns (uint256[]) {
    uint256 _balance = cryptantCrabToken.balanceOf(_owner);
    uint256[] memory _tokenIds = new uint256[](_balance);

    for(uint256 i = 0 ; i < _balance ; i++) {
      _tokenIds[i] = cryptantCrabToken.tokenOfOwnerByIndex(_owner, i);
    }

    return _tokenIds;
  }

  function getCrab(uint256 _tokenId) external view returns (
    uint256 _gene,
    uint256 _level,
    uint256 _exp,
    uint256 _mutationCount,
    uint256 _trophyCount,
    uint256 _heartValue,
    uint256 _growthValue,
    uint256 _fossilType
  ) {
    require(cryptantCrabToken.exists(_tokenId));

    (_gene, _level, _exp, _mutationCount, _trophyCount, _heartValue, _growthValue) = _getCrabData(_tokenId);
    _fossilType = cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_tokenId, "fossilType")));
  }

  function getCrabStats(uint256 _tokenId) external view returns (
    uint256 _hp,
    uint256 _dps,
    uint256 _block,
    uint256[] _partBonuses,
    uint256 _fossilAttribute
  ) {
    require(cryptantCrabToken.exists(_tokenId));

    uint256 _gene = _geneOfCrab(_tokenId);
    (_hp, _dps, _block) = _getCrabTotalStats(_gene);
    _partBonuses = _getCrabPartBonuses(_tokenId);
    _fossilAttribute = cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_tokenId, "fossilAttribute")));
  }

  function _getCrabTotalStats(uint256 _gene) internal view returns (
    uint256 _hp, 
    uint256 _dps,
    uint256 _blockRate
  ) {
    CrabPartData[] memory crabPartData = _getCrabPartData(_gene);

    for(uint256 i = 0 ; i < crabPartData.length ; i++) {
      _hp += crabPartData[i].hp;
      _dps += crabPartData[i].dps;
      _blockRate += crabPartData[i].blockRate;
    }
  }

  function _getCrabPartBonuses(uint256 _tokenId) internal view returns (uint256[] _partBonuses) {
    bytes32[] memory _keys = new bytes32[](4);
    _keys[0] = keccak256(abi.encodePacked(_tokenId, uint256(1), "partBonus"));
    _keys[1] = keccak256(abi.encodePacked(_tokenId, uint256(2), "partBonus"));
    _keys[2] = keccak256(abi.encodePacked(_tokenId, uint256(3), "partBonus"));
    _keys[3] = keccak256(abi.encodePacked(_tokenId, uint256(4), "partBonus"));
    _partBonuses = cryptantCrabStorage.readUint256s(_keys);
  }

  function _getCrabPartData(uint256 _gene) internal view returns (CrabPartData[] memory _crabPartData) {
    require(cryptantCrabToken != address(0));
    uint256[] memory _bodyData;
    uint256[] memory _legData;
    uint256[] memory _leftClawData;
    uint256[] memory _rightClawData;
    
    (_bodyData, _legData, _leftClawData, _rightClawData) = cryptantCrabToken.crabPartDataFromGene(_gene);

    _crabPartData = new CrabPartData[](4);
    _crabPartData[0] = arrayToCrabPartData(_bodyData);
    _crabPartData[1] = arrayToCrabPartData(_legData);
    _crabPartData[2] = arrayToCrabPartData(_leftClawData);
    _crabPartData[3] = arrayToCrabPartData(_rightClawData);
  }
}

contract CryptantCrabPurchasableLaunch is CryptantCrabInformant {
  using SafeMath for uint256;

  Transmuter public transmuter;

  event CrabHatched(address indexed owner, uint256 tokenId, uint256 gene, uint256 specialSkin, uint256 crabPrice, uint256 growthValue);
  event CryptantFragmentsAdded(address indexed cryptantOwner, uint256 amount, uint256 newBalance);
  event CryptantFragmentsRemoved(address indexed cryptantOwner, uint256 amount, uint256 newBalance);
  event Refund(address indexed refundReceiver, uint256 reqAmt, uint256 paid, uint256 refundAmt);

  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress,
    address _transmuterAddress
  ) 
  public 
  CryptantCrabInformant
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress
  ) {
    // constructor
    if(_transmuterAddress != address(0)) {
      _setTransmuterAddress(_transmuterAddress);
    }
  }

  function setAddresses(
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress,
    address _transmuterAddress
  ) 
  external onlyOwner {
    _setAddresses(_genesisCrabAddress, _cryptantCrabTokenAddress, _cryptantCrabStorageAddress);

    if(_transmuterAddress != address(0)) {
      _setTransmuterAddress(_transmuterAddress);
    }
  }

  function _setTransmuterAddress(address _transmuterAddress) internal {
    Transmuter _transmuterContract = Transmuter(_transmuterAddress);
    transmuter = _transmuterContract;
  }

  function getCryptantFragments(address _sender) public view returns (uint256) {
    return cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_sender, "cryptant")));
  }

  function createCrab(uint256 _customTokenId, uint256 _crabPrice, uint256 _customGene, uint256 _customSkin, bool _hasLegendary) external onlyOwner {
    _createCrab(_customTokenId, _crabPrice, _customGene, _customSkin, _hasLegendary);
  }
  function _addCryptantFragments(address _cryptantOwner, uint256 _amount) internal returns (uint256 _newBalance) {
    _newBalance = getCryptantFragments(_cryptantOwner).add(_amount);
    cryptantCrabStorage.updateUint256(keccak256(abi.encodePacked(_cryptantOwner, "cryptant")), _newBalance);
    emit CryptantFragmentsAdded(_cryptantOwner, _amount, _newBalance);
  }

  function _removeCryptantFragments(address _cryptantOwner, uint256 _amount) internal returns (uint256 _newBalance) {
    _newBalance = getCryptantFragments(_cryptantOwner).sub(_amount);
    cryptantCrabStorage.updateUint256(keccak256(abi.encodePacked(_cryptantOwner, "cryptant")), _newBalance);
    emit CryptantFragmentsRemoved(_cryptantOwner, _amount, _newBalance);
  }

  function _createCrab(uint256 _tokenId, uint256 _crabPrice, uint256 _customGene, uint256 _customSkin, bool _hasLegendary) internal {
    uint256[] memory _values = new uint256[](8);
    bytes32[] memory _keys = new bytes32[](8);

    uint256 _gene;
    uint256 _specialSkin;
    uint256 _heartValue;
    uint256 _growthValue;
    if(_customGene == 0) {
      (_gene, _specialSkin, _heartValue, _growthValue) = genesisCrab.generateCrabGene(false, _hasLegendary);
    } else {
      _gene = _customGene;
    }

    if(_customSkin != 0) {
      _specialSkin = _customSkin;
    }

    (_heartValue, _growthValue) = genesisCrab.generateCrabHeart();
    
    cryptantCrabToken.mintToken(msg.sender, _tokenId, _specialSkin);

    // Gene pair
    _keys[0] = keccak256(abi.encodePacked(_tokenId, "gene"));
    _values[0] = _gene;

    // Level pair
    _keys[1] = keccak256(abi.encodePacked(_tokenId, "level"));
    _values[1] = 1;

    // Heart Value pair
    _keys[2] = keccak256(abi.encodePacked(_tokenId, "heartValue"));
    _values[2] = _heartValue;

    // Growth Value pair
    _keys[3] = keccak256(abi.encodePacked(_tokenId, "growthValue"));
    _values[3] = _growthValue;

    // Handling Legendary Bonus
    uint256[] memory _partLegendaryBonuses = transmuter.generateBonusForGene(_gene);
    // body
    _keys[4] = keccak256(abi.encodePacked(_tokenId, uint256(1), "partBonus"));
    _values[4] = _partLegendaryBonuses[0];

    // legs
    _keys[5] = keccak256(abi.encodePacked(_tokenId, uint256(2), "partBonus"));
    _values[5] = _partLegendaryBonuses[1];

    // left claw
    _keys[6] = keccak256(abi.encodePacked(_tokenId, uint256(3), "partBonus"));
    _values[6] = _partLegendaryBonuses[2];

    // right claw
    _keys[7] = keccak256(abi.encodePacked(_tokenId, uint256(4), "partBonus"));
    _values[7] = _partLegendaryBonuses[3];

    require(cryptantCrabStorage.createUint256s(_keys, _values));

    emit CrabHatched(msg.sender, _tokenId, _gene, _specialSkin, _crabPrice, _growthValue);
  }

  function _refundExceededValue(uint256 _senderValue, uint256 _requiredValue) internal {
    uint256 _exceededValue = _senderValue.sub(_requiredValue);

    if(_exceededValue > 0) {
      msg.sender.transfer(_exceededValue);

      emit Refund(msg.sender, _requiredValue, _senderValue, _exceededValue);
    } 
  }
}

contract CryptantInformant is CryptantCrabInformant {
  using SafeMath for uint256;

  event CryptantFragmentsAdded(address indexed cryptantOwner, uint256 amount, uint256 newBalance);
  event CryptantFragmentsRemoved(address indexed cryptantOwner, uint256 amount, uint256 newBalance);

  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress
  ) 
  public 
  CryptantCrabInformant
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress
  ) {
    // constructor

  }

  function getCryptantFragments(address _sender) public view returns (uint256) {
    return cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_sender, "cryptant")));
  }

  function _addCryptantFragments(address _cryptantOwner, uint256 _amount) internal returns (uint256 _newBalance) {
    _newBalance = getCryptantFragments(_cryptantOwner).add(_amount);
    cryptantCrabStorage.updateUint256(keccak256(abi.encodePacked(_cryptantOwner, "cryptant")), _newBalance);
    emit CryptantFragmentsAdded(_cryptantOwner, _amount, _newBalance);
  }

  function _removeCryptantFragments(address _cryptantOwner, uint256 _amount) internal returns (uint256 _newBalance) {
    _newBalance = getCryptantFragments(_cryptantOwner).sub(_amount);
    cryptantCrabStorage.updateUint256(keccak256(abi.encodePacked(_cryptantOwner, "cryptant")), _newBalance);
    emit CryptantFragmentsRemoved(_cryptantOwner, _amount, _newBalance);
  }
}

contract Transmuter is CryptantInformant, GeneSurgeon, Randomable, LevelCalculator {
  event Xenografted(address indexed tokenOwner, uint256 recipientTokenId, uint256 donorTokenId, uint256 oldPartGene, uint256 newPartGene, uint256 oldPartBonus, uint256 newPartBonus, uint256 xenograftPart);
  event Mutated(address indexed tokenOwner, uint256 tokenId, uint256 partIndex, uint256 oldGene, uint256 newGene, uint256 oldPartBonus, uint256 newPartBonus, uint256 mutationCount);

  /**
   * @dev Pre-generated keys to save gas
   * keys are generated with:
   * NORMAL_FOSSIL_RELIC_PERCENTAGE     = bytes4(keccak256("normalFossilRelicPercentage"))    = 0xcaf6fae2
   * PIONEER_FOSSIL_RELIC_PERCENTAGE    = bytes4(keccak256("pioneerFossilRelicPercentage"))   = 0x04988c65
   * LEGENDARY_FOSSIL_RELIC_PERCENTAGE  = bytes4(keccak256("legendaryFossilRelicPercentage")) = 0x277e613a
   * FOSSIL_ATTRIBUTE_COUNT             = bytes4(keccak256("fossilAttributesCount"))          = 0x06c475be
   * LEGENDARY_BONUS_COUNT              = bytes4(keccak256("legendaryBonusCount"))            = 0x45025094
   * LAST_PIONEER_TOKEN_ID              = bytes4(keccak256("lastPioneerTokenId"))             = 0xe562bae2
   */
  bytes4 internal constant NORMAL_FOSSIL_RELIC_PERCENTAGE = 0xcaf6fae2;
  bytes4 internal constant PIONEER_FOSSIL_RELIC_PERCENTAGE = 0x04988c65;
  bytes4 internal constant LEGENDARY_FOSSIL_RELIC_PERCENTAGE = 0x277e613a;
  bytes4 internal constant FOSSIL_ATTRIBUTE_COUNT = 0x06c475be;
  bytes4 internal constant LEGENDARY_BONUS_COUNT = 0x45025094;
  bytes4 internal constant LAST_PIONEER_TOKEN_ID = 0xe562bae2;

  mapping(bytes4 => uint256) internal internalUintVariable;

  // elements => legendary set index of that element
  mapping(uint256 => uint256[]) internal legendaryPartIndex;

  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress
  ) 
  public 
  CryptantInformant
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress
  ) {
    // constructor

    // default values for relic percentages
    // normal crab relic is set to 5%
    _setUint(NORMAL_FOSSIL_RELIC_PERCENTAGE, 5000);

    // pioneer crab relic is set to 50%
    _setUint(PIONEER_FOSSIL_RELIC_PERCENTAGE, 50000);

    // legendary crab part relic is set to increase by 50%
    _setUint(LEGENDARY_FOSSIL_RELIC_PERCENTAGE, 50000);

    // The max number of attributes types
    // Every fossil will have 1 attribute
    _setUint(FOSSIL_ATTRIBUTE_COUNT, 6);

    // The max number of bonus types for legendary
    // Every legendary will have 1 bonus
    _setUint(LEGENDARY_BONUS_COUNT, 5);

    // The last pioneer token ID to be referred as Pioneer
    _setUint(LAST_PIONEER_TOKEN_ID, 1121);
  }

  function setPartIndex(uint256 _element, uint256[] _partIndexes) external onlyOwner {
    legendaryPartIndex[_element] = _partIndexes;
  }

  function getPartIndexes(uint256 _element) external view onlyOwner returns (uint256[] memory _partIndexes){
    _partIndexes = legendaryPartIndex[_element];
  }

  function getUint(bytes4 key) external view returns (uint256 value) {
    value = _getUint(key);
  }

  function setUint(bytes4 key, uint256 value) external onlyOwner {
    _setUint(key, value);
  }

  function _getUint(bytes4 key) internal view returns (uint256 value) {
    value = internalUintVariable[key];
  }

  function _setUint(bytes4 key, uint256 value) internal {
    internalUintVariable[key] = value;
  }

  function xenograft(uint256 _recipientTokenId, uint256 _donorTokenId, uint256 _xenograftPart) external {
    // get crab gene of both token
    // make sure both token is not fossil
    // replace the recipient part with donor part
    // mark donor as fosil
    // fosil will generate 1 attr
    // 3% of fosil will have relic
    // deduct 10 cryptant
    require(_xenograftPart != 1);  // part cannot be body (part index = 1)
    require(cryptantCrabToken.ownerOf(_recipientTokenId) == msg.sender);  // check ownership of both token
    require(cryptantCrabToken.ownerOf(_donorTokenId) == msg.sender);

    // due to stack too deep, need to use an array
    // to represent all the variables
    uint256[] memory _intValues = new uint256[](11);
    _intValues[0] = getCryptantFragments(msg.sender);
    // _intValues[0] = ownedCryptant
    // _intValues[1] = donorPartBonus
    // _intValues[2] = recipientGene
    // _intValues[3] = donorGene
    // _intValues[4] = recipientPart
    // _intValues[5] = donorPart
    // _intValues[6] = relicPercentage
    // _intValues[7] = fossilType
    // _intValues[8] = recipientExistingPartBonus
    // _intValues[9] = recipientLevel
    // _intValues[10] = recipientExp

    // perform transplant requires 5 cryptant
    require(_intValues[0] >= 5000);

    // make sure both tokens are not fossil
    uint256[] memory _values;
    bytes32[] memory _keys = new bytes32[](6);

    _keys[0] = keccak256(abi.encodePacked(_recipientTokenId, "fossilType"));
    _keys[1] = keccak256(abi.encodePacked(_donorTokenId, "fossilType"));
    _keys[2] = keccak256(abi.encodePacked(_donorTokenId, _xenograftPart, "partBonus"));
    _keys[3] = keccak256(abi.encodePacked(_recipientTokenId, _xenograftPart, "partBonus"));
    _keys[4] = keccak256(abi.encodePacked(_recipientTokenId, "level"));
    _keys[5] = keccak256(abi.encodePacked(_recipientTokenId, "exp"));
    _values = cryptantCrabStorage.readUint256s(_keys);

    require(_values[0] == 0);
    require(_values[1] == 0);

    _intValues[1] = _values[2];
    _intValues[8] = _values[3];

    // _values[5] = recipient Exp
    // _values[4] = recipient Level
    _intValues[9] = _values[4];
    _intValues[10] = _values[5];

    // Increase Exp
    _intValues[10] += 8;

    // check if crab level up
    uint256 _expRequired = expRequiredToReachLevel(_intValues[9] + 1);
    if(_intValues[10] >=_expRequired) {
      // increase level
      _intValues[9] += 1;

      // carry forward extra exp
      _intValues[10] -= _expRequired;

      emit LevelUp(msg.sender, _recipientTokenId, _intValues[9], _intValues[10]);
    } else {
      emit ExpGained(msg.sender, _recipientTokenId, _intValues[9], _intValues[10]);
    }

    // start performing Xenograft
    _intValues[2] = _geneOfCrab(_recipientTokenId);
    _intValues[3] = _geneOfCrab(_donorTokenId);

    // recipientPart
    _intValues[4] = _intValues[2] / crabPartMultiplier[_xenograftPart] % 1000;
    _intValues[5] = _intValues[3] / crabPartMultiplier[_xenograftPart] % 1000;
    
    int256 _partDiff = int256(_intValues[4]) - int256(_intValues[5]);
    _intValues[2] = uint256(int256(_intValues[2]) - (_partDiff * int256(crabPartMultiplier[_xenograftPart])));
    
    _values = new uint256[](6);
    _keys = new bytes32[](6);

    // Gene pair
    _keys[0] = keccak256(abi.encodePacked(_recipientTokenId, "gene"));
    _values[0] = _intValues[2];

    // Fossil Attribute
    _keys[1] = keccak256(abi.encodePacked(_donorTokenId, "fossilAttribute"));
    _values[1] = _generateRandomNumber(bytes32(_intValues[2] + _intValues[3] + _xenograftPart), _getUint(FOSSIL_ATTRIBUTE_COUNT)) + 1;

    
    // intVar1 will now use to store relic percentage variable
    if(isLegendaryPart(_intValues[3], 1)) {
      // if body part is legendary 100% become relic
      _intValues[7] = 2;
    } else {
      // Relic percentage will differ depending on the crab type / rarity
      _intValues[6] = _getUint(NORMAL_FOSSIL_RELIC_PERCENTAGE);

      if(_donorTokenId <= _getUint(LAST_PIONEER_TOKEN_ID)) {
        _intValues[6] = _getUint(PIONEER_FOSSIL_RELIC_PERCENTAGE);
      }

      if(isLegendaryPart(_intValues[3], 2) ||
        isLegendaryPart(_intValues[3], 3) || isLegendaryPart(_intValues[3], 4)) {
        _intValues[6] += _getUint(LEGENDARY_FOSSIL_RELIC_PERCENTAGE);
      }

      // Fossil Type
      // 1 = Normal Fossil
      // 2 = Relic Fossil
      _intValues[7] = 1;
      if(_generateRandomNumber(bytes32(_intValues[3] + _xenograftPart), 100000) < _intValues[6]) {
        _intValues[7] = 2;
      }
    }

    _keys[2] = keccak256(abi.encodePacked(_donorTokenId, "fossilType"));
    _values[2] = _intValues[7];

    // Part Attribute
    _keys[3] = keccak256(abi.encodePacked(_recipientTokenId, _xenograftPart, "partBonus"));
    _values[3] = _intValues[1];

    // Recipient Level
    _keys[4] = keccak256(abi.encodePacked(_recipientTokenId, "level"));
    _values[4] = _intValues[9];

    // Recipient Exp
    _keys[5] = keccak256(abi.encodePacked(_recipientTokenId, "exp"));
    _values[5] = _intValues[10];

    require(cryptantCrabStorage.updateUint256s(_keys, _values));

    _removeCryptantFragments(msg.sender, 5000);

    emit Xenografted(msg.sender, _recipientTokenId, _donorTokenId, _intValues[4], _intValues[5], _intValues[8], _intValues[1], _xenograftPart);
  }

  function mutate(uint256 _tokenId, uint256 _partIndex) external {
    // token must be owned by sender
    require(cryptantCrabToken.ownerOf(_tokenId) == msg.sender);
    // body part cannot mutate
    require(_partIndex > 1 && _partIndex < 5);

    // here not checking if sender has enough cryptant
    // is because _removeCryptantFragments uses safeMath
    // to do subtract, so it will revert if it's not enough
    _removeCryptantFragments(msg.sender, 1000);

    bytes32[] memory _keys = new bytes32[](5);
    _keys[0] = keccak256(abi.encodePacked(_tokenId, "gene"));
    _keys[1] = keccak256(abi.encodePacked(_tokenId, "level"));
    _keys[2] = keccak256(abi.encodePacked(_tokenId, "exp"));
    _keys[3] = keccak256(abi.encodePacked(_tokenId, "mutationCount"));
    _keys[4] = keccak256(abi.encodePacked(_tokenId, _partIndex, "partBonus"));

    uint256[] memory _values = new uint256[](5);
    (_values[0], _values[1], _values[2], _values[3], , , ) = _getCrabData(_tokenId);

    uint256[] memory _partsGene = new uint256[](5);
    uint256 i;
    for(i = 1 ; i <= 4 ; i++) {
      _partsGene[i] = _values[0] / crabPartMultiplier[i] % 1000;
    }

    // mutate starts from 3%, max is 20% which is 170 mutations
    if(_values[3] > 170) {
      _values[3] = 170;
    }

    uint256 newPartGene = genesisCrab.mutateCrabPart(_partIndex, _partsGene[_partIndex], (30 + _values[3]) * 100);

    //generate the new gene
    uint256 _oldPartBonus = cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(_tokenId, _partIndex, "partBonus")));
    uint256 _partGene;  // this variable will be reused by oldGene
    uint256 _newGene;
    for(i = 1 ; i <= 4 ; i++) {
      _partGene = _partsGene[i];

      if(i == _partIndex) {
        _partGene = newPartGene;
      }

      _newGene += _partGene * crabPartMultiplier[i];
    }

    if(isLegendaryPart(_newGene, _partIndex)) {
      _values[4] = _generateRandomNumber(bytes32(_newGene + _partIndex + _tokenId), _getUint(LEGENDARY_BONUS_COUNT)) + 1;
    }

    // Reuse partGene as old gene
    _partGene = _values[0];

    // New Gene
    _values[0] = _newGene;

    // Increase Exp
    _values[2] += 8;

    // check if crab level up
    uint256 _expRequired = expRequiredToReachLevel(_values[1] + 1);
    if(_values[2] >=_expRequired) {
      // increase level
      _values[1] += 1;

      // carry forward extra exp
      _values[2] -= _expRequired;

      emit LevelUp(msg.sender, _tokenId, _values[1], _values[2]);
    } else {
      emit ExpGained(msg.sender, _tokenId, _values[1], _values[2]);
    }

    // Increase Mutation Count
    _values[3] += 1;

    require(cryptantCrabStorage.updateUint256s(_keys, _values));

    emit Mutated(msg.sender, _tokenId, _partIndex, _partGene, _newGene, _oldPartBonus, _values[4], _values[3]);
  }

  function generateBonusForGene(uint256 _gene) external view returns (uint256[] _bonuses) {
    _bonuses = new uint256[](4);
    uint256[] memory _elements = extractElementsFromGene(_gene);
    uint256[] memory _parts = extractPartsFromGene(_gene);    
    uint256[] memory _legendaryParts;

    for(uint256 i = 0 ; i < 4 ; i++) {
      _legendaryParts = legendaryPartIndex[_elements[i]];

      for(uint256 j = 0 ; j < _legendaryParts.length ; j++) {
        if(_legendaryParts[j] == _parts[i]) {
          // generate the bonus number and add it into the _bonuses array
          _bonuses[i] = _generateRandomNumber(bytes32(_gene + i), _getUint(LEGENDARY_BONUS_COUNT)) + 1;
          break;
        }
      }
    }
  }

  /**
   * @dev checks if the specified part of the given gene is a legendary part or not
   * returns true if its a legendary part, false otherwise.
   * @param _gene full body gene to be checked on
   * @param _part partIndex ranging from 1 = body, 2 = legs, 3 = left claw, 4 = right claw
   */
  function isLegendaryPart(uint256 _gene, uint256 _part) internal view returns (bool) {
    uint256[] memory _legendaryParts = legendaryPartIndex[extractElementsFromGene(_gene)[_part - 1]];
    for(uint256 i = 0 ; i < _legendaryParts.length ; i++) {
      if(_legendaryParts[i] == extractPartsFromGene(_gene)[_part - 1]) {
        return true;
      }
    }
    return false;
  }
}

contract Withdrawable is Ownable {
  address public withdrawer;

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

  function setWithdrawer(address _newWithdrawer) external onlyOwner {
    withdrawer = _newWithdrawer;
  }

  /**
   * @dev withdraw the specified amount of ether from contract.
   * @param _amount the amount of ether to withdraw. Units in wei.
   */
  function withdraw(uint256 _amount) external onlyWithdrawer returns(bool) {
    require(_amount <= address(this).balance);
    withdrawer.transfer(_amount);
    return true;
  }
}

contract CryptantCrabMarket is CryptantCrabPurchasableLaunch, GeneSurgeon, Randomable, Withdrawable {
  event Purchased(address indexed owner, uint256 amount, uint256 cryptant, uint256 refund);
  event ReferralPurchase(address indexed referral, uint256 rewardAmount, address buyer);
  event CrabOnSaleStarted(address indexed seller, uint256 tokenId, uint256 sellingPrice, uint256 marketId, uint256 gene);
  event CrabOnSaleCancelled(address indexed seller, uint256 tokenId, uint256 marketId);
  event Traded(address indexed seller, address indexed buyer, uint256 tokenId, uint256 tradedPrice, uint256 marketId);   // Trade Type 0 = Purchase

  struct MarketItem {
    uint256 tokenId;
    uint256 sellingPrice;
    address seller;
    uint8 state;              // 1 - on going, 2 - cancelled, 3 - completed
  }

  PrizePool public prizePool;

  /**
   * @dev Pre-generated keys to save gas
   * keys are generated with:
   * MARKET_PRICE_UPDATE_PERIOD = bytes4(keccak256("marketPriceUpdatePeriod"))  = 0xf1305a10
   * CURRENT_TOKEN_ID           = bytes4(keccak256("currentTokenId"))           = 0x21339464
   * REFERRAL_CUT               = bytes4(keccak256("referralCut"))              = 0x40b0b13e
   * PURCHASE_PRIZE_POOL_CUT    = bytes4(keccak256("purchasePrizePoolCut"))     = 0x7625c58a
   * EXCHANGE_PRIZE_POOL_CUT    = bytes4(keccak256("exchangePrizePoolCut"))     = 0xb9e1adb0
   * EXCHANGE_DEVELOPER_CUT     = bytes4(keccak256("exchangeDeveloperCut"))     = 0xfe9ad0eb
   * LAST_TRANSACTION_PERIOD    = bytes4(keccak256("lastTransactionPeriod"))    = 0x1a01d5bb
   * LAST_TRANSACTION_PRICE     = bytes4(keccak256("lastTransactionPrice"))     = 0xf14adb6a
   */
  bytes4 internal constant MARKET_PRICE_UPDATE_PERIOD = 0xf1305a10;
  bytes4 internal constant CURRENT_TOKEN_ID = 0x21339464;
  bytes4 internal constant REFERRAL_CUT = 0x40b0b13e;
  bytes4 internal constant PURCHASE_PRIZE_POOL_CUT = 0x7625c58a;
  bytes4 internal constant EXCHANGE_PRIZE_POOL_CUT = 0xb9e1adb0;
  bytes4 internal constant EXCHANGE_DEVELOPER_CUT = 0xfe9ad0eb;
  bytes4 internal constant LAST_TRANSACTION_PERIOD = 0x1a01d5bb;
  bytes4 internal constant LAST_TRANSACTION_PRICE = 0xf14adb6a;

  /**
   * @dev The first 25 trading crab price will be fixed to 0.3 ether.
   * This only applies to crab bought from developer.
   * Crab on auction will depends on the price set by owner.
   */
  uint256 constant public initialCrabTradingPrice = 300 finney;
  
  // The initial cryptant price will be fixed to 0.03 ether.
  // It will changed to dynamic price after 25 crabs traded.
  // 1000 Cryptant Fragment = 1 Cryptant.
  uint256 constant public initialCryptantFragmentTradingPrice = 30 szabo;

  mapping(bytes4 => uint256) internal internalUintVariable;

  // All traded price will be stored here
  uint256[] public tradedPrices;

  // All auctioned items will be stored here
  MarketItem[] public marketItems;

  // PrizePool key, default value is 0xadd5d43f
  // 0xadd5d43f = bytes4(keccak256(bytes("firstPrizePool")));
  bytes4 public currentPrizePool = 0xadd5d43f;

  constructor
  (
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress,
    address _transmuterAddress,
    address _prizePoolAddress
  ) 
  public 
  CryptantCrabPurchasableLaunch
  (
    _genesisCrabAddress, 
    _cryptantCrabTokenAddress, 
    _cryptantCrabStorageAddress,
    _transmuterAddress
  ) {
    // constructor
    if(_prizePoolAddress != address(0)) {
      _setPrizePoolAddress(_prizePoolAddress);
    }
    
    // set the initial token id
    _setUint(CURRENT_TOKEN_ID, 1121);

    // The number of seconds that the market will stay at fixed price. 
    // Default set to 4 hours
    _setUint(MARKET_PRICE_UPDATE_PERIOD, 14400);

    // The percentage of referral cut
    // Default set to 10%
    _setUint(REFERRAL_CUT, 10000);

    // The percentage of price pool cut when purchase a new crab
    // Default set to 20%
    _setUint(PURCHASE_PRIZE_POOL_CUT, 20000);

    // The percentage of prize pool cut when market exchange traded
    // Default set to 2%
    _setUint(EXCHANGE_PRIZE_POOL_CUT, 2000);

    // The percentage of developer cut
    // Default set to 2.8%
    _setUint(EXCHANGE_DEVELOPER_CUT, 2800);

    // to prevent marketId = 0
    // put a dummy value for it
    marketItems.push(MarketItem(0, 0, address(0), 0));
  }

  function _setPrizePoolAddress(address _prizePoolAddress) internal {
    PrizePool _prizePoolContract = PrizePool(_prizePoolAddress);
    prizePool = _prizePoolContract;
  }

  function setAddresses(
    address _genesisCrabAddress, 
    address _cryptantCrabTokenAddress, 
    address _cryptantCrabStorageAddress,
    address _transmuterAddress,
    address _prizePoolAddress
  ) 
  external onlyOwner {
    _setAddresses(_genesisCrabAddress, _cryptantCrabTokenAddress, _cryptantCrabStorageAddress);

    if(_transmuterAddress != address(0)) {
      _setTransmuterAddress(_transmuterAddress);
    }

    if(_prizePoolAddress != address(0)) {
      _setPrizePoolAddress(_prizePoolAddress);
    }
  }

  function setCurrentPrizePool(bytes4 _newPrizePool) external onlyOwner {
    currentPrizePool = _newPrizePool;
  }

  function getUint(bytes4 key) external view returns (uint256 value) {
    value = _getUint(key);
  }

  function setUint(bytes4 key, uint256 value) external onlyOwner {
    _setUint(key, value);
  }

  function _getUint(bytes4 key) internal view returns (uint256 value) {
    value = internalUintVariable[key];
  }

  function _setUint(bytes4 key, uint256 value) internal {
    internalUintVariable[key] = value;
  }

  function purchase(uint256 _crabAmount, uint256 _cryptantFragmentAmount, address _referral) external payable {
    require(_crabAmount >= 0 && _crabAmount <= 10 );
    require(_cryptantFragmentAmount >= 0 && _cryptantFragmentAmount <= 10000);
    require(!(_crabAmount == 0 && _cryptantFragmentAmount == 0));
    require(_cryptantFragmentAmount % 1000 == 0);
    require(msg.sender != _referral);

    // check if ether payment is enough
    uint256 _singleCrabPrice = getCurrentCrabPrice();
    uint256 _totalCrabPrice = _singleCrabPrice * _crabAmount;
    uint256 _totalCryptantPrice = getCurrentCryptantFragmentPrice() * _cryptantFragmentAmount;
    uint256 _cryptantFragmentsGained = _cryptantFragmentAmount;

    // free 2 cryptant when purchasing 10
    if(_cryptantFragmentsGained == 10000) {
      _cryptantFragmentsGained += 2000;
    }

    uint256 _totalPrice = _totalCrabPrice + _totalCryptantPrice;
    uint256 _value = msg.value;

    require(_value >= _totalPrice);

    // Purchase 10 crabs will have 1 crab with legendary part
    // Default value for _crabWithLegendaryPart is just a unreacable number
    uint256 _currentTokenId = _getUint(CURRENT_TOKEN_ID);
    uint256 _crabWithLegendaryPart = 100;
    if(_crabAmount == 10) {
      // decide which crab will have the legendary part
      _crabWithLegendaryPart = _generateRandomNumber(bytes32(_currentTokenId), 10);
    }

    for(uint256 i = 0 ; i < _crabAmount ; i++) {
      // 5000 ~ 5500 is gift token
      // so if hit 5000 will skip to 5500 onwards
      if(_currentTokenId == 5000) {
        _currentTokenId = 5500;
      }

      _currentTokenId++;
      _createCrab(_currentTokenId, _singleCrabPrice, 0, 0, _crabWithLegendaryPart == i);
      tradedPrices.push(_singleCrabPrice);
    }

    if(_cryptantFragmentsGained > 0) {
      _addCryptantFragments(msg.sender, (_cryptantFragmentsGained));
    }

    _setUint(CURRENT_TOKEN_ID, _currentTokenId);
    
    // Refund exceeded value
    _refundExceededValue(_value, _totalPrice);

    // If there's referral, will transfer the referral reward to the referral
    if(_referral != address(0)) {
      uint256 _referralReward = _totalPrice * _getUint(REFERRAL_CUT) / 100000;
      _referral.transfer(_referralReward);
      emit ReferralPurchase(_referral, _referralReward, msg.sender);
    }

    // Send prize pool cut to prize pool
    uint256 _prizePoolAmount = _totalPrice * _getUint(PURCHASE_PRIZE_POOL_CUT) / 100000;
    prizePool.increasePrizePool.value(_prizePoolAmount)(currentPrizePool);

    _setUint(LAST_TRANSACTION_PERIOD, now / _getUint(MARKET_PRICE_UPDATE_PERIOD));
    _setUint(LAST_TRANSACTION_PRICE, _singleCrabPrice);

    emit Purchased(msg.sender, _crabAmount, _cryptantFragmentsGained, _value - _totalPrice);
  }

  function getCurrentPeriod() external view returns (uint256 _now, uint256 _currentPeriod) {
    _now = now;
    _currentPeriod = now / _getUint(MARKET_PRICE_UPDATE_PERIOD);
  }

  function getCurrentCrabPrice() public view returns (uint256) {
    if(totalCrabTraded() > 25) {
      uint256 _lastTransactionPeriod = _getUint(LAST_TRANSACTION_PERIOD);
      uint256 _lastTransactionPrice = _getUint(LAST_TRANSACTION_PRICE);

      if(_lastTransactionPeriod == now / _getUint(MARKET_PRICE_UPDATE_PERIOD) && _lastTransactionPrice != 0) {
        return _lastTransactionPrice;
      } else {
        uint256 totalPrice;
        for(uint256 i = 1 ; i <= 15 ; i++) {
          totalPrice += tradedPrices[tradedPrices.length - i];
        }

        // the actual calculation here is:
        // average price = totalPrice / 15
        return totalPrice / 15;
      }
    } else {
      return initialCrabTradingPrice;
    }
  }

  function getCurrentCryptantFragmentPrice() public view returns (uint256 _price) {
    if(totalCrabTraded() > 25) {
      // real calculation is 1 Cryptant = 10% of currentCrabPrice
      // should be written as getCurrentCrabPrice() * 10 / 100 / 1000
      return getCurrentCrabPrice() * 10 / 100000;
    } else {
      return initialCryptantFragmentTradingPrice;
    }
  }

  // After pre-sale crab tracking (excluding fossil transactions)
  function totalCrabTraded() public view returns (uint256) {
    return tradedPrices.length;
  }

  function sellCrab(uint256 _tokenId, uint256 _sellingPrice) external {
    require(cryptantCrabToken.ownerOf(_tokenId) == msg.sender);
    require(_sellingPrice >= 50 finney && _sellingPrice <= 100 ether);

    marketItems.push(MarketItem(_tokenId, _sellingPrice, msg.sender, 1));

    // escrow
    cryptantCrabToken.transferFrom(msg.sender, address(this), _tokenId);

    uint256 _gene = _geneOfCrab(_tokenId);

    emit CrabOnSaleStarted(msg.sender, _tokenId, _sellingPrice, marketItems.length - 1, _gene);
  }

  function cancelOnSaleCrab(uint256 _marketId) external {
    MarketItem storage marketItem = marketItems[_marketId];

    // Only able to cancel on sale Item
    require(marketItem.state == 1);

    // Set Market Item state to 2(Cancelled)
    marketItem.state = 2;

    // Only owner can cancel on sale item
    require(marketItem.seller == msg.sender);

    // Release escrow to the owner
    cryptantCrabToken.transferFrom(address(this), msg.sender, marketItem.tokenId);

    emit CrabOnSaleCancelled(msg.sender, marketItem.tokenId, _marketId);
  }

  function buyCrab(uint256 _marketId) external payable {
    MarketItem storage marketItem = marketItems[_marketId];
    require(marketItem.state == 1);   // make sure the sale is on going
    require(marketItem.sellingPrice == msg.value);
    require(marketItem.seller != msg.sender);

    cryptantCrabToken.safeTransferFrom(address(this), msg.sender, marketItem.tokenId);

    uint256 _developerCut = msg.value * _getUint(EXCHANGE_DEVELOPER_CUT) / 100000;
    uint256 _prizePoolCut = msg.value * _getUint(EXCHANGE_PRIZE_POOL_CUT) / 100000;
    uint256 _sellerAmount = msg.value - _developerCut - _prizePoolCut;
    marketItem.seller.transfer(_sellerAmount);

    // Send prize pool cut to prize pool
    prizePool.increasePrizePool.value(_prizePoolCut)(currentPrizePool);

    uint256 _fossilType = cryptantCrabStorage.readUint256(keccak256(abi.encodePacked(marketItem.tokenId, "fossilType")));
    if(_fossilType > 0) {
      tradedPrices.push(marketItem.sellingPrice);
    }

    marketItem.state = 3;

    _setUint(LAST_TRANSACTION_PERIOD, now / _getUint(MARKET_PRICE_UPDATE_PERIOD));
    _setUint(LAST_TRANSACTION_PRICE, getCurrentCrabPrice());

    emit Traded(marketItem.seller, msg.sender, marketItem.tokenId, marketItem.sellingPrice, _marketId);
  }

  function() public payable {
    revert();
  }
}

contract HasNoEther is Ownable {

  /**
  * @dev Constructor that rejects incoming Ether
  * The `payable` flag is added so we can access `msg.value` without compiler warning. If we
  * leave out payable, then Solidity will allow inheriting contracts to implement a payable
  * constructor. By doing it this way we prevent a payable constructor from working. Alternatively
  * we could use assembly to access msg.value.
  */
  constructor() public payable {
    require(msg.value == 0);
  }

  /**
   * @dev Disallows direct send by settings a default function without the `payable` flag.
   */
  function() external {
  }

  /**
   * @dev Transfer all Ether held by the contract to the owner.
   */
  function reclaimEther() external onlyOwner {
    owner.transfer(address(this).balance);
  }
}

contract RBAC {
  using Roles for Roles.Role;

  mapping (string => Roles.Role) private roles;

  event RoleAdded(address indexed operator, string role);
  event RoleRemoved(address indexed operator, string role);

  /**
   * @dev reverts if addr does not have role
   * @param _operator address
   * @param _role the name of the role
   * // reverts
   */
  function checkRole(address _operator, string _role)
    view
    public
  {
    roles[_role].check(_operator);
  }

  /**
   * @dev determine if addr has role
   * @param _operator address
   * @param _role the name of the role
   * @return bool
   */
  function hasRole(address _operator, string _role)
    view
    public
    returns (bool)
  {
    return roles[_role].has(_operator);
  }

  /**
   * @dev add a role to an address
   * @param _operator address
   * @param _role the name of the role
   */
  function addRole(address _operator, string _role)
    internal
  {
    roles[_role].add(_operator);
    emit RoleAdded(_operator, _role);
  }

  /**
   * @dev remove a role from an address
   * @param _operator address
   * @param _role the name of the role
   */
  function removeRole(address _operator, string _role)
    internal
  {
    roles[_role].remove(_operator);
    emit RoleRemoved(_operator, _role);
  }

  /**
   * @dev modifier to scope access to a single role (uses msg.sender as addr)
   * @param _role the name of the role
   * // reverts
   */
  modifier onlyRole(string _role)
  {
    checkRole(msg.sender, _role);
    _;
  }

  /**
   * @dev modifier to scope access to a set of roles (uses msg.sender as addr)
   * @param _roles the names of the roles to scope access to
   * // reverts
   *
   * @TODO - when solidity supports dynamic arrays as arguments to modifiers, provide this
   *  see: https://github.com/ethereum/solidity/issues/2467
   */
  // modifier onlyRoles(string[] _roles) {
  //     bool hasAnyRole = false;
  //     for (uint8 i = 0; i < _roles.length; i++) {
  //         if (hasRole(msg.sender, _roles[i])) {
  //             hasAnyRole = true;
  //             break;
  //         }
  //     }

  //     require(hasAnyRole);

  //     _;
  // }
}

contract Whitelist is Ownable, RBAC {
  string public constant ROLE_WHITELISTED = "whitelist";

  /**
   * @dev Throws if operator is not whitelisted.
   * @param _operator address
   */
  modifier onlyIfWhitelisted(address _operator) {
    checkRole(_operator, ROLE_WHITELISTED);
    _;
  }

  /**
   * @dev add an address to the whitelist
   * @param _operator address
   * @return true if the address was added to the whitelist, false if the address was already in the whitelist
   */
  function addAddressToWhitelist(address _operator)
    onlyOwner
    public
  {
    addRole(_operator, ROLE_WHITELISTED);
  }

  /**
   * @dev getter to determine if address is in whitelist
   */
  function whitelist(address _operator)
    public
    view
    returns (bool)
  {
    return hasRole(_operator, ROLE_WHITELISTED);
  }

  /**
   * @dev add addresses to the whitelist
   * @param _operators addresses
   * @return true if at least one address was added to the whitelist,
   * false if all addresses were already in the whitelist
   */
  function addAddressesToWhitelist(address[] _operators)
    onlyOwner
    public
  {
    for (uint256 i = 0; i < _operators.length; i++) {
      addAddressToWhitelist(_operators[i]);
    }
  }

  /**
   * @dev remove an address from the whitelist
   * @param _operator address
   * @return true if the address was removed from the whitelist,
   * false if the address wasn't in the whitelist in the first place
   */
  function removeAddressFromWhitelist(address _operator)
    onlyOwner
    public
  {
    removeRole(_operator, ROLE_WHITELISTED);
  }

  /**
   * @dev remove addresses from the whitelist
   * @param _operators addresses
   * @return true if at least one address was removed from the whitelist,
   * false if all addresses weren't in the whitelist in the first place
   */
  function removeAddressesFromWhitelist(address[] _operators)
    onlyOwner
    public
  {
    for (uint256 i = 0; i < _operators.length; i++) {
      removeAddressFromWhitelist(_operators[i]);
    }
  }

}

contract PrizePool is Ownable, Whitelist, HasNoEther {
  event PrizePoolIncreased(uint256 amountIncreased, bytes4 prizePool, uint256 currentAmount);
  event WinnerAdded(address winner, bytes4 prizeTitle, uint256 claimableAmount);
  event PrizedClaimed(address winner, bytes4 prizeTitle, uint256 claimedAmount);

  // prizePool key => prizePool accumulated amount
  // this is just to track how much a prizePool has
  mapping(bytes4 => uint256) prizePools;

  // winner's address => prize title => amount
  // prize title itself need to be able to determine
  // the prize pool it is from
  mapping(address => mapping(bytes4 => uint256)) winners;

  constructor() public {

  }

  function increasePrizePool(bytes4 _prizePool) external payable onlyIfWhitelisted(msg.sender) {
    prizePools[_prizePool] += msg.value;

    emit PrizePoolIncreased(msg.value, _prizePool, prizePools[_prizePool]);
  }

  function addWinner(address _winner, bytes4 _prizeTitle, uint256 _claimableAmount) external onlyIfWhitelisted(msg.sender) {
    winners[_winner][_prizeTitle] = _claimableAmount;

    emit WinnerAdded(_winner, _prizeTitle, _claimableAmount);
  }

  function claimPrize(bytes4 _prizeTitle) external {
    uint256 _claimableAmount = winners[msg.sender][_prizeTitle];

    require(_claimableAmount > 0);

    msg.sender.transfer(_claimableAmount);

    winners[msg.sender][_prizeTitle] = 0;

    emit PrizedClaimed(msg.sender, _prizeTitle, _claimableAmount);
  }

  function claimableAmount(address _winner, bytes4 _prizeTitle) external view returns (uint256 _claimableAmount) {
    _claimableAmount = winners[_winner][_prizeTitle];
  }

  function prizePoolTotal(bytes4 _prizePool) external view returns (uint256 _prizePoolTotal) {
    _prizePoolTotal = prizePools[_prizePool];
  }
}

library Roles {
  struct Role {
    mapping (address => bool) bearer;
  }

  /**
   * @dev give an address access to this role
   */
  function add(Role storage role, address addr)
    internal
  {
    role.bearer[addr] = true;
  }

  /**
   * @dev remove an address' access to this role
   */
  function remove(Role storage role, address addr)
    internal
  {
    role.bearer[addr] = false;
  }

  /**
   * @dev check if an address has this role
   * // reverts
   */
  function check(Role storage role, address addr)
    view
    internal
  {
    require(has(role, addr));
  }

  /**
   * @dev check if an address has this role
   * @return bool
   */
  function has(Role storage role, address addr)
    view
    internal
    returns (bool)
  {
    return role.bearer[addr];
  }
}

contract ERC721Basic is ERC165 {
  event Transfer(
    address indexed _from,
    address indexed _to,
    uint256 indexed _tokenId
  );
  event Approval(
    address indexed _owner,
    address indexed _approved,
    uint256 indexed _tokenId
  );
  event ApprovalForAll(
    address indexed _owner,
    address indexed _operator,
    bool _approved
  );

  function balanceOf(address _owner) public view returns (uint256 _balance);
  function ownerOf(uint256 _tokenId) public view returns (address _owner);
  function exists(uint256 _tokenId) public view returns (bool _exists);

  function approve(address _to, uint256 _tokenId) public;
  function getApproved(uint256 _tokenId)
    public view returns (address _operator);

  function setApprovalForAll(address _operator, bool _approved) public;
  function isApprovedForAll(address _owner, address _operator)
    public view returns (bool);

  function transferFrom(address _from, address _to, uint256 _tokenId) public;
  function safeTransferFrom(address _from, address _to, uint256 _tokenId)
    public;

  function safeTransferFrom(
    address _from,
    address _to,
    uint256 _tokenId,
    bytes _data
  )
    public;
}

contract ERC721Enumerable is ERC721Basic {
  function totalSupply() public view returns (uint256);
  function tokenOfOwnerByIndex(
    address _owner,
    uint256 _index
  )
    public
    view
    returns (uint256 _tokenId);

  function tokenByIndex(uint256 _index) public view returns (uint256);
}

contract ERC721Metadata is ERC721Basic {
  function name() external view returns (string _name);
  function symbol() external view returns (string _symbol);
  function tokenURI(uint256 _tokenId) public view returns (string);
}

contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata {
}

contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {

  bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;
  /*
   * 0x80ac58cd ===
   *   bytes4(keccak256('balanceOf(address)')) ^
   *   bytes4(keccak256('ownerOf(uint256)')) ^
   *   bytes4(keccak256('approve(address,uint256)')) ^
   *   bytes4(keccak256('getApproved(uint256)')) ^
   *   bytes4(keccak256('setApprovalForAll(address,bool)')) ^
   *   bytes4(keccak256('isApprovedForAll(address,address)')) ^
   *   bytes4(keccak256('transferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
   */

  bytes4 private constant InterfaceId_ERC721Exists = 0x4f558e79;
  /*
   * 0x4f558e79 ===
   *   bytes4(keccak256('exists(uint256)'))
   */

  using SafeMath for uint256;
  using AddressUtils for address;

  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
  // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
  bytes4 private constant ERC721_RECEIVED = 0x150b7a02;

  // Mapping from token ID to owner
  mapping (uint256 => address) internal tokenOwner;

  // Mapping from token ID to approved address
  mapping (uint256 => address) internal tokenApprovals;

  // Mapping from owner to number of owned token
  mapping (address => uint256) internal ownedTokensCount;

  // Mapping from owner to operator approvals
  mapping (address => mapping (address => bool)) internal operatorApprovals;

  /**
   * @dev Guarantees msg.sender is owner of the given token
   * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
   */
  modifier onlyOwnerOf(uint256 _tokenId) {
    require(ownerOf(_tokenId) == msg.sender);
    _;
  }

  /**
   * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
   * @param _tokenId uint256 ID of the token to validate
   */
  modifier canTransfer(uint256 _tokenId) {
    require(isApprovedOrOwner(msg.sender, _tokenId));
    _;
  }

  constructor()
    public
  {
    // register the supported interfaces to conform to ERC721 via ERC165
    _registerInterface(InterfaceId_ERC721);
    _registerInterface(InterfaceId_ERC721Exists);
  }

  /**
   * @dev Gets the balance of the specified address
   * @param _owner address to query the balance of
   * @return uint256 representing the amount owned by the passed address
   */
  function balanceOf(address _owner) public view returns (uint256) {
    require(_owner != address(0));
    return ownedTokensCount[_owner];
  }

  /**
   * @dev Gets the owner of the specified token ID
   * @param _tokenId uint256 ID of the token to query the owner of
   * @return owner address currently marked as the owner of the given token ID
   */
  function ownerOf(uint256 _tokenId) public view returns (address) {
    address owner = tokenOwner[_tokenId];
    require(owner != address(0));
    return owner;
  }

  /**
   * @dev Returns whether the specified token exists
   * @param _tokenId uint256 ID of the token to query the existence of
   * @return whether the token exists
   */
  function exists(uint256 _tokenId) public view returns (bool) {
    address owner = tokenOwner[_tokenId];
    return owner != address(0);
  }

  /**
   * @dev Approves another address to transfer the given token ID
   * The zero address indicates there is no approved address.
   * There can only be one approved address per token at a given time.
   * Can only be called by the token owner or an approved operator.
   * @param _to address to be approved for the given token ID
   * @param _tokenId uint256 ID of the token to be approved
   */
  function approve(address _to, uint256 _tokenId) public {
    address owner = ownerOf(_tokenId);
    require(_to != owner);
    require(msg.sender == owner || isApprovedForAll(owner, msg.sender));

    tokenApprovals[_tokenId] = _to;
    emit Approval(owner, _to, _tokenId);
  }

  /**
   * @dev Gets the approved address for a token ID, or zero if no address set
   * @param _tokenId uint256 ID of the token to query the approval of
   * @return address currently approved for the given token ID
   */
  function getApproved(uint256 _tokenId) public view returns (address) {
    return tokenApprovals[_tokenId];
  }

  /**
   * @dev Sets or unsets the approval of a given operator
   * An operator is allowed to transfer all tokens of the sender on their behalf
   * @param _to operator address to set the approval
   * @param _approved representing the status of the approval to be set
   */
  function setApprovalForAll(address _to, bool _approved) public {
    require(_to != msg.sender);
    operatorApprovals[msg.sender][_to] = _approved;
    emit ApprovalForAll(msg.sender, _to, _approved);
  }

  /**
   * @dev Tells whether an operator is approved by a given owner
   * @param _owner owner address which you want to query the approval of
   * @param _operator operator address which you want to query the approval of
   * @return bool whether the given operator is approved by the given owner
   */
  function isApprovedForAll(
    address _owner,
    address _operator
  )
    public
    view
    returns (bool)
  {
    return operatorApprovals[_owner][_operator];
  }

  /**
   * @dev Transfers the ownership of a given token ID to another address
   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
   * Requires the msg sender to be the owner, approved, or operator
   * @param _from current owner of the token
   * @param _to address to receive the ownership of the given token ID
   * @param _tokenId uint256 ID of the token to be transferred
  */
  function transferFrom(
    address _from,
    address _to,
    uint256 _tokenId
  )
    public
    canTransfer(_tokenId)
  {
    require(_from != address(0));
    require(_to != address(0));

    clearApproval(_from, _tokenId);
    removeTokenFrom(_from, _tokenId);
    addTokenTo(_to, _tokenId);

    emit Transfer(_from, _to, _tokenId);
  }

  /**
   * @dev Safely transfers the ownership of a given token ID to another address
   * If the target address is a contract, it must implement `onERC721Received`,
   * which is called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
   * the transfer is reverted.
   *
   * Requires the msg sender to be the owner, approved, or operator
   * @param _from current owner of the token
   * @param _to address to receive the ownership of the given token ID
   * @param _tokenId uint256 ID of the token to be transferred
  */
  function safeTransferFrom(
    address _from,
    address _to,
    uint256 _tokenId
  )
    public
    canTransfer(_tokenId)
  {
    // solium-disable-next-line arg-overflow
    safeTransferFrom(_from, _to, _tokenId, "");
  }

  /**
   * @dev Safely transfers the ownership of a given token ID to another address
   * If the target address is a contract, it must implement `onERC721Received`,
   * which is called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
   * the transfer is reverted.
   * Requires the msg sender to be the owner, approved, or operator
   * @param _from current owner of the token
   * @param _to address to receive the ownership of the given token ID
   * @param _tokenId uint256 ID of the token to be transferred
   * @param _data bytes data to send along with a safe transfer check
   */
  function safeTransferFrom(
    address _from,
    address _to,
    uint256 _tokenId,
    bytes _data
  )
    public
    canTransfer(_tokenId)
  {
    transferFrom(_from, _to, _tokenId);
    // solium-disable-next-line arg-overflow
    require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
  }

  /**
   * @dev Returns whether the given spender can transfer a given token ID
   * @param _spender address of the spender to query
   * @param _tokenId uint256 ID of the token to be transferred
   * @return bool whether the msg.sender is approved for the given token ID,
   *  is an operator of the owner, or is the owner of the token
   */
  function isApprovedOrOwner(
    address _spender,
    uint256 _tokenId
  )
    internal
    view
    returns (bool)
  {
    address owner = ownerOf(_tokenId);
    // Disable solium check because of
    // https://github.com/duaraghav8/Solium/issues/175
    // solium-disable-next-line operator-whitespace
    return (
      _spender == owner ||
      getApproved(_tokenId) == _spender ||
      isApprovedForAll(owner, _spender)
    );
  }

  /**
   * @dev Internal function to mint a new token
   * Reverts if the given token ID already exists
   * @param _to The address that will own the minted token
   * @param _tokenId uint256 ID of the token to be minted by the msg.sender
   */
  function _mint(address _to, uint256 _tokenId) internal {
    require(_to != address(0));
    addTokenTo(_to, _tokenId);
    emit Transfer(address(0), _to, _tokenId);
  }

  /**
   * @dev Internal function to burn a specific token
   * Reverts if the token does not exist
   * @param _tokenId uint256 ID of the token being burned by the msg.sender
   */
  function _burn(address _owner, uint256 _tokenId) internal {
    clearApproval(_owner, _tokenId);
    removeTokenFrom(_owner, _tokenId);
    emit Transfer(_owner, address(0), _tokenId);
  }

  /**
   * @dev Internal function to clear current approval of a given token ID
   * Reverts if the given address is not indeed the owner of the token
   * @param _owner owner of the token
   * @param _tokenId uint256 ID of the token to be transferred
   */
  function clearApproval(address _owner, uint256 _tokenId) internal {
    require(ownerOf(_tokenId) == _owner);
    if (tokenApprovals[_tokenId] != address(0)) {
      tokenApprovals[_tokenId] = address(0);
    }
  }

  /**
   * @dev Internal function to add a token ID to the list of a given address
   * @param _to address representing the new owner of the given token ID
   * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
   */
  function addTokenTo(address _to, uint256 _tokenId) internal {
    require(tokenOwner[_tokenId] == address(0));
    tokenOwner[_tokenId] = _to;
    ownedTokensCount[_to] = ownedTokensCount[_to].add(1);
  }

  /**
   * @dev Internal function to remove a token ID from the list of a given address
   * @param _from address representing the previous owner of the given token ID
   * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
   */
  function removeTokenFrom(address _from, uint256 _tokenId) internal {
    require(ownerOf(_tokenId) == _from);
    ownedTokensCount[_from] = ownedTokensCount[_from].sub(1);
    tokenOwner[_tokenId] = address(0);
  }

  /**
   * @dev Internal function to invoke `onERC721Received` on a target address
   * The call is not executed if the target address is not a contract
   * @param _from address representing the previous owner of the given token ID
   * @param _to target address that will receive the tokens
   * @param _tokenId uint256 ID of the token to be transferred
   * @param _data bytes optional data to send along with the call
   * @return whether the call correctly returned the expected magic value
   */
  function checkAndCallSafeTransfer(
    address _from,
    address _to,
    uint256 _tokenId,
    bytes _data
  )
    internal
    returns (bool)
  {
    if (!_to.isContract()) {
      return true;
    }
    bytes4 retval = ERC721Receiver(_to).onERC721Received(
      msg.sender, _from, _tokenId, _data);
    return (retval == ERC721_RECEIVED);
  }
}

contract ERC721Receiver {
  /**
   * @dev Magic value to be returned upon successful reception of an NFT
   *  Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
   *  which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
   */
  bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;

  /**
   * @notice Handle the receipt of an NFT
   * @dev The ERC721 smart contract calls this function on the recipient
   * after a `safetransfer`. This function MAY throw to revert and reject the
   * transfer. Return of other than the magic value MUST result in the 
   * transaction being reverted.
   * Note: the contract address is always the message sender.
   * @param _operator The address which called `safeTransferFrom` function
   * @param _from The address which previously owned the token
   * @param _tokenId The NFT identifier which is being transfered
   * @param _data Additional data with no specified format
   * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
   */
  function onERC721Received(
    address _operator,
    address _from,
    uint256 _tokenId,
    bytes _data
  )
    public
    returns(bytes4);
}

contract ERC721Token is SupportsInterfaceWithLookup, ERC721BasicToken, ERC721 {

  bytes4 private constant InterfaceId_ERC721Enumerable = 0x780e9d63;
  /**
   * 0x780e9d63 ===
   *   bytes4(keccak256('totalSupply()')) ^
   *   bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
   *   bytes4(keccak256('tokenByIndex(uint256)'))
   */

  bytes4 private constant InterfaceId_ERC721Metadata = 0x5b5e139f;
  /**
   * 0x5b5e139f ===
   *   bytes4(keccak256('name()')) ^
   *   bytes4(keccak256('symbol()')) ^
   *   bytes4(keccak256('tokenURI(uint256)'))
   */

  // Token name
  string internal name_;

  // Token symbol
  string internal symbol_;

  // 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;

  // Array with all token ids, used for enumeration
  uint256[] internal allTokens;

  // Mapping from token id to position in the allTokens array
  mapping(uint256 => uint256) internal allTokensIndex;

  // Optional mapping for token URIs
  mapping(uint256 => string) internal tokenURIs;

  /**
   * @dev Constructor function
   */
  constructor(string _name, string _symbol) public {
    name_ = _name;
    symbol_ = _symbol;

    // register the supported interfaces to conform to ERC721 via ERC165
    _registerInterface(InterfaceId_ERC721Enumerable);
    _registerInterface(InterfaceId_ERC721Metadata);
  }

  /**
   * @dev Gets the token name
   * @return string representing the token name
   */
  function name() external view returns (string) {
    return name_;
  }

  /**
   * @dev Gets the token symbol
   * @return string representing the token symbol
   */
  function symbol() external view returns (string) {
    return symbol_;
  }

  /**
   * @dev Returns an URI for a given token ID
   * Throws if the token ID does not exist. May return an empty string.
   * @param _tokenId uint256 ID of the token to query
   */
  function tokenURI(uint256 _tokenId) public view returns (string) {
    require(exists(_tokenId));
    return tokenURIs[_tokenId];
  }

  /**
   * @dev Gets the token ID at a given index of the tokens list of the requested owner
   * @param _owner address owning the tokens list to be accessed
   * @param _index uint256 representing the index to be accessed of the requested tokens list
   * @return uint256 token ID at the given index of the tokens list owned by the requested address
   */
  function tokenOfOwnerByIndex(
    address _owner,
    uint256 _index
  )
    public
    view
    returns (uint256)
  {
    require(_index < balanceOf(_owner));
    return ownedTokens[_owner][_index];
  }

  /**
   * @dev Gets the total amount of tokens stored by the contract
   * @return uint256 representing the total amount of tokens
   */
  function totalSupply() public view returns (uint256) {
    return allTokens.length;
  }

  /**
   * @dev Gets the token ID at a given index of all the tokens in this contract
   * Reverts if the index is greater or equal to the total number of tokens
   * @param _index uint256 representing the index to be accessed of the tokens list
   * @return uint256 token ID at the given index of the tokens list
   */
  function tokenByIndex(uint256 _index) public view returns (uint256) {
    require(_index < totalSupply());
    return allTokens[_index];
  }

  /**
   * @dev Internal function to set the token URI for a given token
   * Reverts if the token ID does not exist
   * @param _tokenId uint256 ID of the token to set its URI
   * @param _uri string URI to assign
   */
  function _setTokenURI(uint256 _tokenId, string _uri) internal {
    require(exists(_tokenId));
    tokenURIs[_tokenId] = _uri;
  }

  /**
   * @dev Internal function to add a token ID to the list of a given address
   * @param _to address representing the new owner of the given token ID
   * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
   */
  function addTokenTo(address _to, uint256 _tokenId) internal {
    super.addTokenTo(_to, _tokenId);
    uint256 length = ownedTokens[_to].length;
    ownedTokens[_to].push(_tokenId);
    ownedTokensIndex[_tokenId] = length;
  }

  /**
   * @dev Internal function to remove a token ID from the list of a given address
   * @param _from address representing the previous owner of the given token ID
   * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
   */
  function removeTokenFrom(address _from, uint256 _tokenId) internal {
    super.removeTokenFrom(_from, _tokenId);

    uint256 tokenIndex = ownedTokensIndex[_tokenId];
    uint256 lastTokenIndex = ownedTokens[_from].length.sub(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;
  }

  /**
   * @dev Internal function to mint a new token
   * Reverts if the given token ID already exists
   * @param _to address the beneficiary that will own the minted token
   * @param _tokenId uint256 ID of the token to be minted by the msg.sender
   */
  function _mint(address _to, uint256 _tokenId) internal {
    super._mint(_to, _tokenId);

    allTokensIndex[_tokenId] = allTokens.length;
    allTokens.push(_tokenId);
  }

  /**
   * @dev Internal function to burn a specific token
   * 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 {
    super._burn(_owner, _tokenId);

    // Clear metadata (if any)
    if (bytes(tokenURIs[_tokenId]).length != 0) {
      delete tokenURIs[_tokenId];
    }

    // Reorg all tokens array
    uint256 tokenIndex = allTokensIndex[_tokenId];
    uint256 lastTokenIndex = allTokens.length.sub(1);
    uint256 lastToken = allTokens[lastTokenIndex];

    allTokens[tokenIndex] = lastToken;
    allTokens[lastTokenIndex] = 0;

    allTokens.length--;
    allTokensIndex[_tokenId] = 0;
    allTokensIndex[lastToken] = tokenIndex;
  }

}

contract CryptantCrabNFT is ERC721Token, Whitelist, CrabData, GeneSurgeon {
  event CrabPartAdded(uint256 hp, uint256 dps, uint256 blockAmount);
  event GiftTransfered(address indexed _from, address indexed _to, uint256 indexed _tokenId);
  event DefaultMetadataURIChanged(string newUri);

  /**
   * @dev Pre-generated keys to save gas
   * keys are generated with:
   * CRAB_BODY       = bytes4(keccak256("crab_body"))       = 0xc398430e
   * CRAB_LEG        = bytes4(keccak256("crab_leg"))        = 0x889063b1
   * CRAB_LEFT_CLAW  = bytes4(keccak256("crab_left_claw"))  = 0xdb6290a2
   * CRAB_RIGHT_CLAW = bytes4(keccak256("crab_right_claw")) = 0x13453f89
   */
  bytes4 internal constant CRAB_BODY = 0xc398430e;
  bytes4 internal constant CRAB_LEG = 0x889063b1;
  bytes4 internal constant CRAB_LEFT_CLAW = 0xdb6290a2;
  bytes4 internal constant CRAB_RIGHT_CLAW = 0x13453f89;

  /**
   * @dev Stores all the crab data
   */
  mapping(bytes4 => mapping(uint256 => CrabPartData[])) internal crabPartData;

  /**
   * @dev Mapping from tokenId to its corresponding special skin
   * tokenId with default skin will not be stored. 
   */
  mapping(uint256 => uint256) internal crabSpecialSkins;

  /**
   * @dev default MetadataURI
   */
  string public defaultMetadataURI = "https://www.cryptantcrab.io/md/";

  constructor(string _name, string _symbol) public ERC721Token(_name, _symbol) {
    // constructor
    initiateCrabPartData();
  }

  /**
   * @dev Returns an URI for a given token ID
   * Throws if the token ID does not exist.
   * Will return the token's metadata URL if it has one, 
   * otherwise will just return base on the default metadata URI
   * @param _tokenId uint256 ID of the token to query
   */
  function tokenURI(uint256 _tokenId) public view returns (string) {
    require(exists(_tokenId));

    string memory _uri = tokenURIs[_tokenId];

    if(bytes(_uri).length == 0) {
      _uri = getMetadataURL(bytes(defaultMetadataURI), _tokenId);
    }

    return _uri;
  }

  /**
   * @dev Returns the data of a specific parts
   * @param _partIndex the part to retrieve. 1 = Body, 2 = Legs, 3 = Left Claw, 4 = Right Claw
   * @param _element the element of part to retrieve. 1 = Fire, 2 = Earth, 3 = Metal, 4 = Spirit, 5 = Water
   * @param _setIndex the set index of for the specified part. This will starts from 1.
   */
  function dataOfPart(uint256 _partIndex, uint256 _element, uint256 _setIndex) public view returns (uint256[] memory _resultData) {
    bytes4 _key;
    if(_partIndex == 1) {
      _key = CRAB_BODY;
    } else if(_partIndex == 2) {
      _key = CRAB_LEG;
    } else if(_partIndex == 3) {
      _key = CRAB_LEFT_CLAW;
    } else if(_partIndex == 4) {
      _key = CRAB_RIGHT_CLAW;
    } else {
      revert();
    }

    CrabPartData storage _crabPartData = crabPartData[_key][_element][_setIndex];

    _resultData = crabPartDataToArray(_crabPartData);
  }

  /**
   * @dev Gift(Transfer) a token to another address. Caller must be token owner
   * @param _from current owner of the token
   * @param _to address to receive the ownership of the given token ID
   * @param _tokenId uint256 ID of the token to be transferred
   */
  function giftToken(address _from, address _to, uint256 _tokenId) external {
    safeTransferFrom(_from, _to, _tokenId);

    emit GiftTransfered(_from, _to, _tokenId);
  }

  /**
   * @dev External function to mint a new token, for whitelisted address only.
   * Reverts if the given token ID already exists
   * @param _tokenOwner address the beneficiary that will own the minted token
   * @param _tokenId uint256 ID of the token to be minted by the msg.sender
   * @param _skinId the skin ID to be applied for all the token minted
   */
  function mintToken(address _tokenOwner, uint256 _tokenId, uint256 _skinId) external onlyIfWhitelisted(msg.sender) {
    super._mint(_tokenOwner, _tokenId);

    if(_skinId > 0) {
      crabSpecialSkins[_tokenId] = _skinId;
    }
  }

  /**
   * @dev Returns crab data base on the gene provided
   * @param _gene the gene info where crab data will be retrieved base on it
   * @return 4 uint arrays:
   * 1st Array = Body's Data
   * 2nd Array = Leg's Data
   * 3rd Array = Left Claw's Data
   * 4th Array = Right Claw's Data
   */
  function crabPartDataFromGene(uint256 _gene) external view returns (
    uint256[] _bodyData,
    uint256[] _legData,
    uint256[] _leftClawData,
    uint256[] _rightClawData
  ) {
    uint256[] memory _parts = extractPartsFromGene(_gene);
    uint256[] memory _elements = extractElementsFromGene(_gene);

    _bodyData = dataOfPart(1, _elements[0], _parts[0]);
    _legData = dataOfPart(2, _elements[1], _parts[1]);
    _leftClawData = dataOfPart(3, _elements[2], _parts[2]);
    _rightClawData = dataOfPart(4, _elements[3], _parts[3]);
  }

  /**
   * @dev For developer to add new parts, notice that this is the only method to add crab data
   * so that developer can add extra content. there's no other method for developer to modify
   * the data. This is to assure token owner actually owns their data.
   * @param _partIndex the part to add. 1 = Body, 2 = Legs, 3 = Left Claw, 4 = Right Claw
   * @param _element the element of part to add. 1 = Fire, 2 = Earth, 3 = Metal, 4 = Spirit, 5 = Water
   * @param _partDataArray data of the parts.
   */
  function setPartData(uint256 _partIndex, uint256 _element, uint256[] _partDataArray) external onlyOwner {
    CrabPartData memory _partData = arrayToCrabPartData(_partDataArray);

    bytes4 _key;
    if(_partIndex == 1) {
      _key = CRAB_BODY;
    } else if(_partIndex == 2) {
      _key = CRAB_LEG;
    } else if(_partIndex == 3) {
      _key = CRAB_LEFT_CLAW;
    } else if(_partIndex == 4) {
      _key = CRAB_RIGHT_CLAW;
    }

    // if index 1 is empty will fill at index 1
    if(crabPartData[_key][_element][1].hp == 0 && crabPartData[_key][_element][1].dps == 0) {
      crabPartData[_key][_element][1] = _partData;
    } else {
      crabPartData[_key][_element].push(_partData);
    }

    emit CrabPartAdded(_partDataArray[0], _partDataArray[1], _partDataArray[2]);
  }

  /**
   * @dev Updates the default metadata URI
   * @param _defaultUri the new metadata URI
   */
  function setDefaultMetadataURI(string _defaultUri) external onlyOwner {
    defaultMetadataURI = _defaultUri;

    emit DefaultMetadataURIChanged(_defaultUri);
  }

  /**
   * @dev Updates the metadata URI for existing token
   * @param _tokenId the tokenID that metadata URI to be changed
   * @param _uri the new metadata URI for the specified token
   */
  function setTokenURI(uint256 _tokenId, string _uri) external onlyIfWhitelisted(msg.sender) {
    _setTokenURI(_tokenId, _uri);
  }

  /**
   * @dev Returns the special skin of the provided tokenId
   * @param _tokenId cryptant crab's tokenId
   * @return Special skin belongs to the _tokenId provided. 
   * 0 will be returned if no special skin found.
   */
  function specialSkinOfTokenId(uint256 _tokenId) external view returns (uint256) {
    return crabSpecialSkins[_tokenId];
  }

  /**
   * @dev This functions will adjust the length of crabPartData
   * so that when adding data the index can start with 1.
   * Reason of doing this is because gene cannot have parts with index 0.
   */
  function initiateCrabPartData() internal {
    require(crabPartData[CRAB_BODY][1].length == 0);

    for(uint256 i = 1 ; i <= 5 ; i++) {
      crabPartData[CRAB_BODY][i].length = 2;
      crabPartData[CRAB_LEG][i].length = 2;
      crabPartData[CRAB_LEFT_CLAW][i].length = 2;
      crabPartData[CRAB_RIGHT_CLAW][i].length = 2;
    }
  }

  /**
   * @dev Returns whether the given spender can transfer a given token ID
   * @param _spender address of the spender to query
   * @param _tokenId uint256 ID of the token to be transferred
   * @return bool whether the msg.sender is approved for the given token ID,
   *  is an operator of the owner, or is the owner of the token, 
   *  or has been whitelisted by contract owner
   */
  function isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) {
    address owner = ownerOf(_tokenId);
    return _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender) || whitelist(_spender);
  }

  /**
   * @dev Will merge the uri and tokenId together. 
   * @param _uri URI to be merge. This will be the first part of the result URL.
   * @param _tokenId tokenID to be merge. This will be the last part of the result URL.
   * @return the merged urL
   */
  function getMetadataURL(bytes _uri, uint256 _tokenId) internal pure returns (string) {
    uint256 _tmpTokenId = _tokenId;
    uint256 _tokenLength;

    // Getting the length(number of digits) of token ID
    do {
      _tokenLength++;
      _tmpTokenId /= 10;
    } while (_tmpTokenId > 0);

    // creating a byte array with the length of URL + token digits
    bytes memory _result = new bytes(_uri.length + _tokenLength);

    // cloning the uri bytes into the result bytes
    for(uint256 i = 0 ; i < _uri.length ; i ++) {
      _result[i] = _uri[i];
    }

    // appending the tokenId to the end of the result bytes
    uint256 lastIndex = _result.length - 1;
    for(_tmpTokenId = _tokenId ; _tmpTokenId > 0 ; _tmpTokenId /= 10) {
      _result[lastIndex--] = byte(48 + _tmpTokenId % 10);
    }

    return string(_result);
  }
}

Contract Security Audit

Contract ABI

[{"constant":true,"inputs":[],"name":"getCurrentPeriod","outputs":[{"name":"_now","type":"uint256"},{"name":"_currentPeriod","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newWithdrawer","type":"address"}],"name":"setWithdrawer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalCrabTraded","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentCryptantFragmentPrice","outputs":[{"name":"_price","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_genesisCrabAddress","type":"address"},{"name":"_cryptantCrabTokenAddress","type":"address"},{"name":"_cryptantCrabStorageAddress","type":"address"}],"name":"setAddresses","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_genesisCrabAddress","type":"address"},{"name":"_cryptantCrabTokenAddress","type":"address"},{"name":"_cryptantCrabStorageAddress","type":"address"},{"name":"_transmuterAddress","type":"address"}],"name":"setAddresses","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"marketItems","outputs":[{"name":"tokenId","type":"uint256"},{"name":"sellingPrice","type":"uint256"},{"name":"seller","type":"address"},{"name":"state","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_genesisCrabAddress","type":"address"},{"name":"_cryptantCrabTokenAddress","type":"address"},{"name":"_cryptantCrabStorageAddress","type":"address"},{"name":"_transmuterAddress","type":"address"},{"name":"_prizePoolAddress","type":"address"}],"name":"setAddresses","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newPrizePool","type":"bytes4"}],"name":"setCurrentPrizePool","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentPrizePool","outputs":[{"name":"","type":"bytes4"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_marketId","type":"uint256"}],"name":"buyCrab","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"prizePool","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"transmuter","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_customTokenId","type":"uint256"},{"name":"_crabPrice","type":"uint256"},{"name":"_customGene","type":"uint256"},{"name":"_customSkin","type":"uint256"},{"name":"_hasLegendary","type":"bool"}],"name":"createCrab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"genesisCrab","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initialCrabTradingPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"key","type":"bytes4"}],"name":"getUint","outputs":[{"name":"value","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tradedPrices","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_sellingPrice","type":"uint256"}],"name":"sellCrab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"key","type":"bytes4"},{"name":"value","type":"uint256"}],"name":"setUint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_marketId","type":"uint256"}],"name":"cancelOnSaleCrab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentCrabPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"}],"name":"getCryptantFragments","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"withdrawer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cryptantCrabStorage","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initialCryptantFragmentTradingPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cryptantCrabToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_crabAmount","type":"uint256"},{"name":"_cryptantFragmentAmount","type":"uint256"},{"name":"_referral","type":"address"}],"name":"purchase","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_genesisCrabAddress","type":"address"},{"name":"_cryptantCrabTokenAddress","type":"address"},{"name":"_cryptantCrabStorageAddress","type":"address"},{"name":"_transmuterAddress","type":"address"},{"name":"_prizePoolAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"cryptant","type":"uint256"},{"indexed":false,"name":"refund","type":"uint256"}],"name":"Purchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"referral","type":"address"},{"indexed":false,"name":"rewardAmount","type":"uint256"},{"indexed":false,"name":"buyer","type":"address"}],"name":"ReferralPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"sellingPrice","type":"uint256"},{"indexed":false,"name":"marketId","type":"uint256"},{"indexed":false,"name":"gene","type":"uint256"}],"name":"CrabOnSaleStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"marketId","type":"uint256"}],"name":"CrabOnSaleCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"seller","type":"address"},{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"tradedPrice","type":"uint256"},{"indexed":false,"name":"marketId","type":"uint256"}],"name":"Traded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"gene","type":"uint256"},{"indexed":false,"name":"specialSkin","type":"uint256"},{"indexed":false,"name":"crabPrice","type":"uint256"},{"indexed":false,"name":"growthValue","type":"uint256"}],"name":"CrabHatched","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"cryptantOwner","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"newBalance","type":"uint256"}],"name":"CryptantFragmentsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"cryptantOwner","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"newBalance","type":"uint256"}],"name":"CryptantFragmentsRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"refundReceiver","type":"address"},{"indexed":false,"name":"reqAmt","type":"uint256"},{"indexed":false,"name":"paid","type":"uint256"},{"indexed":false,"name":"refundAmt","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]

61012060405260006080908152633b9aca0060a052620f424060c0526103e860e05260016101005262000036906005908162000464565b50600b805463ffffffff191663add5d43f1790553480156200005757600080fd5b5060405160a08062002ebf8339810160409081528151602083015191830151606084015160809094015160008054600160a060020a031916331790559193909184848484838383828282620000b783838364010000000062000353810204565b50505050600160a060020a038316159150620000e3905057620000e381640100000000620003eb810204565b50505050600160a060020a038116156200010b576200010b816401000000006200040d810204565b620001427f21339464000000000000000000000000000000000000000000000000000000006104616401000000006200042f810204565b620001797ff1305a10000000000000000000000000000000000000000000000000000000006138406401000000006200042f810204565b620001b07f40b0b13e000000000000000000000000000000000000000000000000000000006127106401000000006200042f810204565b620001e77f7625c58a00000000000000000000000000000000000000000000000000000000614e206401000000006200042f810204565b6200021e7fb9e1adb0000000000000000000000000000000000000000000000000000000006107d06401000000006200042f810204565b620002557ffe9ad0eb00000000000000000000000000000000000000000000000000000000610af06401000000006200042f810204565b50506040805160808101825260008082526020820181815292820181815260608301828152600a8054600181018255935292517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a860039093029283015592517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a982015591517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2aa90920180549151600160a060020a0319909216600160a060020a039093169290921760a060020a60ff0219167401000000000000000000000000000000000000000060ff9092169190910217905550620004dc915050565b60008080600160a060020a03861615620003865760018054600160a060020a031916600160a060020a0388161790558592505b600160a060020a03851615620003b55760028054600160a060020a031916600160a060020a0387161790558491505b600160a060020a03841615620003e3575060038054600160a060020a031916600160a060020a038516179055825b505050505050565b60048054600160a060020a031916600160a060020a0392909216919091179055565b60078054600160a060020a031916600160a060020a0392909216919091179055565b7fffffffff00000000000000000000000000000000000000000000000000000000909116600090815260086020526040902055565b828054828255906000526020600020908101928215620004aa579160200282015b82811115620004aa578251829063ffffffff1690559160200191906001019062000485565b50620004b8929150620004bc565b5090565b620004d991905b80821115620004b85760008155600101620004c3565b90565b6129d380620004ec6000396000f30060806040526004361061017c5763ffffffff60e060020a600035041663086146d281146101815780630d174c24146101af5780631797ec8a146101d25780632e1a7d4d146101f95780632fbcf56814610225578063363bf9641461023a5780634a945f8d1461026757806351f28e141461029a5780635dd68acd146102e357806364cab0e31461031c578063675f8aea1461033e57806368882b3014610370578063715018a61461037b578063719ce73e146103905780637239d2b9146103c1578063765388ae146103d65780637d9527d6146103fc57806389d6e86f146104115780638da5cb5b14610426578063928e6b161461043b5780639684ebba1461045d5780639bd702e414610475578063a08fcabb14610490578063c6a0b13f146104b5578063c781f97b146104cd578063cc89d596146104e2578063cdc1842414610503578063dede553f14610518578063e30fc0191461052d578063e43e9de014610542578063ea3bd5df14610557578063f2fde38b14610571575b600080fd5b34801561018d57600080fd5b50610196610592565b6040805192835260208301919091528051918290030190f35b3480156101bb57600080fd5b506101d0600160a060020a03600435166105cf565b005b3480156101de57600080fd5b506101e7610608565b60408051918252519081900360200190f35b34801561020557600080fd5b5061021160043561060f565b604080519115158252519081900360200190f35b34801561023157600080fd5b506101e761067a565b34801561024657600080fd5b506101d0600160a060020a03600435811690602435811690604435166106b9565b34801561027357600080fd5b506101d0600160a060020a03600435811690602435811690604435811690606435166106e0565b3480156102a657600080fd5b506102b2600435610720565b604080519485526020850193909352600160a060020a039091168383015260ff166060830152519081900360800190f35b3480156102ef57600080fd5b506101d0600160a060020a0360043581169060243581169060443581169060643581169060843516610777565b34801561032857600080fd5b506101d0600160e060020a0319600435166107d0565b34801561034a57600080fd5b50610353610802565b60408051600160e060020a03199092168252519081900360200190f35b6101d060043561080e565b34801561038757600080fd5b506101d0610cc5565b34801561039c57600080fd5b506103a5610d24565b60408051600160a060020a039092168252519081900360200190f35b3480156103cd57600080fd5b506103a5610d33565b3480156103e257600080fd5b506101d06004356024356044356064356084351515610d42565b34801561040857600080fd5b506103a5610d66565b34801561041d57600080fd5b506101e7610d75565b34801561043257600080fd5b506103a5610d81565b34801561044757600080fd5b506101e7600160e060020a031960043516610d90565b34801561046957600080fd5b506101e7600435610da1565b34801561048157600080fd5b506101d0600435602435610dc0565b34801561049c57600080fd5b506101d0600160e060020a031960043516602435611079565b3480156104c157600080fd5b506101d060043561109e565b3480156104d957600080fd5b506101e76111fe565b3480156104ee57600080fd5b506101e7600160a060020a036004351661130d565b34801561050f57600080fd5b506103a561145e565b34801561052457600080fd5b506103a561146d565b34801561053957600080fd5b506101e761147c565b34801561054e57600080fd5b506103a5611486565b6101d0600435602435600160a060020a0360443516611495565b34801561057d57600080fd5b506101d0600160a060020a0360043516611896565b4260006105be7ff1305a10000000000000000000000000000000000000000000000000000000006118b9565b428115156105c857fe5b0490509091565b600054600160a060020a031633146105e657600080fd5b60068054600160a060020a031916600160a060020a0392909216919091179055565b6009545b90565b600654600090600160a060020a0316331461062957600080fd5b303182111561063757600080fd5b600654604051600160a060020a039091169083156108fc029084906000818181858888f19350505050158015610671573d6000803e3d6000fd5b50600192915050565b60006019610686610608565b11156106ac57620186a06106986111fe565b600a028115156106a457fe5b04905061060c565b50651b48eb57e00061060c565b600054600160a060020a031633146106d057600080fd5b6106db8383836118d5565b505050565b600054600160a060020a031633146106f757600080fd5b6107028484846118d5565b600160a060020a0381161561071a5761071a8161196a565b50505050565b600a80548290811061072e57fe5b6000918252602090912060039091020180546001820154600290920154909250600160a060020a0381169074010000000000000000000000000000000000000000900460ff1684565b600054600160a060020a0316331461078e57600080fd5b6107998585856118d5565b600160a060020a038216156107b1576107b18261196a565b600160a060020a038116156107c9576107c98161198c565b5050505050565b600054600160a060020a031633146107e757600080fd5b600b805463ffffffff191660e060020a909204919091179055565b600b5460e060020a0281565b6000806000806000600a8681548110151561082557fe5b906000526020600020906003020194508460020160149054906101000a900460ff1660ff16600114151561085857600080fd5b6001850154341461086857600080fd5b6002850154600160a060020a031633141561088257600080fd5b6002548554604080517f42842e0e000000000000000000000000000000000000000000000000000000008152306004820152336024820152604481019290925251600160a060020a03909216916342842e0e9160648082019260009290919082900301818387803b1580156108f657600080fd5b505af115801561090a573d6000803e3d6000fd5b50505050620186a061092563fe9ad0eb60e060020a026118b9565b340281151561093057fe5b049350620186a06109607fb9e1adb0000000000000000000000000000000000000000000000000000000006118b9565b340281151561096b57fe5b6002870154604051929091049450348690038590039350600160a060020a03169083156108fc029084906000818181858888f193505050501580156109b4573d6000803e3d6000fd5b50600754600b54604080517f035186d600000000000000000000000000000000000000000000000000000000815260e060020a909202600160e060020a031916600483015251600160a060020a039092169163035186d6918691602480830192600092919082900301818588803b158015610a2e57600080fd5b505af1158015610a42573d6000803e3d6000fd5b50506003548854604080516020808201939093527f666f7373696c5479706500000000000000000000000000000000000000000000818301528151602a818303018152604a909101918290528051600160a060020a0390941696506389853691955093509182918401908083835b60208310610acf5780518252601f199092019160209182019101610ab0565b51815160209384036101000a60001901801990921691161790526040805192909401829003822063ffffffff881660e060020a0283526004830152925160248083019650939450929083900301905081600087803b158015610b3057600080fd5b505af1158015610b44573d6000803e3d6000fd5b505050506040513d6020811015610b5a57600080fd5b505190506000811115610b9f576001808601546009805492830181556000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af909101555b60028501805474ff0000000000000000000000000000000000000000191674030000000000000000000000000000000000000000179055610c337f1a01d5bb00000000000000000000000000000000000000000000000000000000610c237ff1305a10000000000000000000000000000000000000000000000000000000006118b9565b42811515610c2d57fe5b046119ae565b610c647ff14adb6a00000000000000000000000000000000000000000000000000000000610c5f6111fe565b6119ae565b600285015485546001870154604080519283526020830191909152818101899052513392600160a060020a0316917f825f67dcf8bc9df74117166defc432f4cdf0a331eff757dd049327c83c2e02e1919081900360600190a3505050505050565b600054600160a060020a03163314610cdc57600080fd5b60008054604051600160a060020a03909116917ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482091a260008054600160a060020a0319169055565b600754600160a060020a031681565b600454600160a060020a031681565b600054600160a060020a03163314610d5957600080fd5b6107c985858585856119cb565b600154600160a060020a031681565b670429d069189e000081565b600054600160a060020a031681565b6000610d9b826118b9565b92915050565b6009805482908110610daf57fe5b600091825260209091200154905081565b600254604080517f6352211e0000000000000000000000000000000000000000000000000000000081526004810185905290516000923392600160a060020a0390911691636352211e9160248082019260209290919082900301818887803b158015610e2b57600080fd5b505af1158015610e3f573d6000803e3d6000fd5b505050506040513d6020811015610e5557600080fd5b5051600160a060020a031614610e6a57600080fd5b66b1a2bc2ec500008210158015610e8a575068056bc75e2d631000008211155b1515610e9557600080fd5b604080516080810182528481526020810184815233828401818152600160608501818152600a80549283018155600090815295517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a860039093029283015593517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a982015590517fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2aa90910180549351600160a060020a0319909416600160a060020a039283161774ff000000000000000000000000000000000000000019167401000000000000000000000000000000000000000060ff909516949094029390931790925560025484517f23b872dd000000000000000000000000000000000000000000000000000000008152600481019290925230602483015260448201889052935193909116926323b872dd9260648084019391929182900301818387803b15801561100157600080fd5b505af1158015611015573d6000803e3d6000fd5b5050505061102283612589565b600a54604080518681526020810186905260001990920182820152606082018390525191925033917fe85f50d04aad3a08dbccb8010694abf96aea1d1b4bad25ee468c18fc0b8a67ce9181900360800190a2505050565b600054600160a060020a0316331461109057600080fd5b61109a82826119ae565b5050565b6000600a828154811015156110af57fe5b906000526020600020906003020190508060020160149054906101000a900460ff1660ff1660011415156110e257600080fd5b6002810180547402000000000000000000000000000000000000000074ff0000000000000000000000000000000000000000199091161790819055600160a060020a0316331461113157600080fd5b6002548154604080517f23b872dd000000000000000000000000000000000000000000000000000000008152306004820152336024820152604481019290925251600160a060020a03909216916323b872dd9160648082019260009290919082900301818387803b1580156111a557600080fd5b505af11580156111b9573d6000803e3d6000fd5b50508254604080519182526020820186905280513394507f60fc417a183f79070cd39314730ea379a973582cd704728681c917fb347895799350918290030190a25050565b60008060008060006019611210610608565b11156112fa5761123f7f1a01d5bb000000000000000000000000000000000000000000000000000000006118b9565b935061126a7ff14adb6a000000000000000000000000000000000000000000000000000000006118b9565b92506112957ff1305a10000000000000000000000000000000000000000000000000000000006118b9565b4281151561129f57fe5b04841480156112ad57508215155b156112ba57829450611306565b5060015b600f81116112ef57600980548281039081106112d657fe5b60009182526020909120015491909101906001016112be565b600f82049450611306565b670429d069189e000094505b5050505090565b6000600360009054906101000a9004600160a060020a0316600160a060020a03166389853691836040516020018082600160a060020a0316600160a060020a03166c01000000000000000000000000028152601401807f6372797074616e740000000000000000000000000000000000000000000000008152506008019150506040516020818303038152906040526040518082805190602001908083835b602083106113cb5780518252601f1990920191602091820191016113ac565b51815160209384036101000a60001901801990921691161790526040805192909401829003822063ffffffff881660e060020a0283526004830152925160248083019650939450929083900301905081600087803b15801561142c57600080fd5b505af1158015611440573d6000803e3d6000fd5b505050506040513d602081101561145657600080fd5b505192915050565b600654600160a060020a031681565b600354600160a060020a031681565b651b48eb57e00081565b600254600160a060020a031681565b6000806000806000806000806000806000808e101580156114b75750600a8e11155b15156114c257600080fd5b60008d101580156114d557506127108d11155b15156114e057600080fd5b8d1580156114ec57508c155b156114f657600080fd5b6103e88d061561150557600080fd5b33600160a060020a038d16141561151b57600080fd5b6115236111fe565b9a508d8b0299508c61153361067a565b0298508c975087612710141561154b576107d0880197505b89890196503495508686101561156057600080fd5b6115897f21339464000000000000000000000000000000000000000000000000000000006118b9565b9450606493508d600a14156115a6576115a385600a612627565b93505b600092505b8d831015611619578461138814156115c35761157c94505b6001909401936115d9858c6000808888146119cb565b60098054600181810183556000929092527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af018c905592909201916115ab565b600088111561162e5761162c3389612644565b505b6116587f2133946400000000000000000000000000000000000000000000000000000000866119ae565b61166286886127d9565b600160a060020a038c161561172957620186a061169e7f40b0b13e000000000000000000000000000000000000000000000000000000006118b9565b88028115156116a957fe5b0491508b600160a060020a03166108fc839081150290604051600060405180830381858888f193505050501580156116e5573d6000803e3d6000fd5b50604080518381523360208201528151600160a060020a038f16927f6344e900bd7e00e72714516a5ca3986dd2d886d3bc769e48272b66dcbfe72610928290030190a25b620186a06117567f7625c58a000000000000000000000000000000000000000000000000000000006118b9565b880281151561176157fe5b600754600b54604080517f035186d600000000000000000000000000000000000000000000000000000000815260e060020a909202600160e060020a031916600483015251939092049350600160a060020a03169163035186d6918491602480830192600092919082900301818588803b1580156117de57600080fd5b505af11580156117f2573d6000803e3d6000fd5b5050505050611818631a01d5bb60e060020a02610c2363f1305a1060e060020a026118b9565b6118427ff14adb6a000000000000000000000000000000000000000000000000000000008c6119ae565b604080518f8152602081018a905288880381830152905133917f2bdd59583c8e5cc64165e86af2482dbe93e85c98b355b788aa592465b3f6920e919081900360600190a25050505050505050505050505050565b600054600160a060020a031633146118ad57600080fd5b6118b68161286c565b50565b600160e060020a03191660009081526008602052604090205490565b60008080600160a060020a038616156119075760018054600160a060020a031916600160a060020a0388161790558592505b600160a060020a038516156119355760028054600160a060020a031916600160a060020a0387161790558491505b600160a060020a03841615611962575060038054600160a060020a031916600160a060020a038516179055825b505050505050565b60048054600160a060020a031916600160a060020a0392909216919091179055565b60078054600160a060020a031916600160a060020a0392909216919091179055565b600160e060020a0319909116600090815260086020526040902055565b60608060008060008060606008604051908082528060200260200182016040528015611a01578160200160208202803883390190505b5060408051600880825261012082019092529198506020820161010080388339019050509550891515611aec57600154604080517f9d0c0c350000000000000000000000000000000000000000000000000000000081526000600482018190528b151560248301529151600160a060020a0390931692639d0c0c3592604480840193608093929083900390910190829087803b158015611aa057600080fd5b505af1158015611ab4573d6000803e3d6000fd5b505050506040513d6080811015611aca57600080fd5b5080516020820151604083015160609093015191975095509093509150611af0565b8994505b8815611afa578893505b600154604080517f6f9fbd7c0000000000000000000000000000000000000000000000000000000081528151600160a060020a0390931692636f9fbd7c926004808401939192918290030181600087803b158015611b5757600080fd5b505af1158015611b6b573d6000803e3d6000fd5b505050506040513d6040811015611b8157600080fd5b8101908080519060200190929190805190602001909291905050508093508194505050600260009054906101000a9004600160a060020a0316600160a060020a03166323a36d2b338e876040518463ffffffff1660e060020a0281526004018084600160a060020a0316600160a060020a031681526020018381526020018281526020019350505050600060405180830381600087803b158015611c2457600080fd5b505af1158015611c38573d6000803e3d6000fd5b505050508b60405160200180828152602001807f67656e65000000000000000000000000000000000000000000000000000000008152506004019150506040516020818303038152906040526040518082805190602001908083835b60208310611cb35780518252601f199092019160209182019101611c94565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020866000815181101515611cef57fe5b602090810290910101528651859088906000908110611d0a57fe5b6020908102909101810191909152604080518083018f90527f6c6576656c0000000000000000000000000000000000000000000000000000008183015281516025818303018152604590910191829052805190928291908401908083835b60208310611d875780518252601f199092019160209182019101611d68565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020866001815181101515611dc357fe5b602090810290910101528651600190889082908110611dde57fe5b6020908102909101810191909152604080518083018f90527f686561727456616c756500000000000000000000000000000000000000000000818301528151602a818303018152604a90910191829052805190928291908401908083835b60208310611e5b5780518252601f199092019160209182019101611e3c565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020866002815181101515611e9757fe5b602090810290910101528651839088906002908110611eb257fe5b6020908102909101810191909152604080518083018f90527f67726f77746856616c7565000000000000000000000000000000000000000000818301528151602b818303018152604b90910191829052805190928291908401908083835b60208310611f2f5780518252601f199092019160209182019101611f10565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020866003815181101515611f6b57fe5b602090810290910101528651829088906003908110611f8657fe5b6020908102909101015260048054604080517fcfc2aad200000000000000000000000000000000000000000000000000000000815292830188905251600160a060020a039091169163cfc2aad291602480830192600092919082900301818387803b158015611ff457600080fd5b505af1158015612008573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561203157600080fd5b81019080805164010000000081111561204957600080fd5b8201602081018481111561205c57600080fd5b815185602082028301116401000000008211171561207957600080fd5b505092919050505090508b60016040516020018083815260200182815260200180600080516020612988833981519152815250600901925050506040516020818303038152906040526040518082805190602001908083835b602083106120f15780518252601f1990920191602091820191016120d2565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902086600481518110151561212d57fe5b6020908102909101015280518190600090811061214657fe5b9060200190602002015187600481518110151561215f57fe5b6020908102909101810191909152604080518083018f9052600281830152600080516020612988833981519152606082015281516049818303018152606990910191829052805190928291908401908083835b602083106121d15780518252601f1990920191602091820191016121b2565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902086600581518110151561220d57fe5b6020908102909101015280518190600190811061222657fe5b9060200190602002015187600581518110151561223f57fe5b6020908102909101810191909152604080518083018f9052600381830152600080516020612988833981519152606082015281516049818303018152606990910191829052805190928291908401908083835b602083106122b15780518252601f199092019160209182019101612292565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390208660068151811015156122ed57fe5b6020908102909101015280518190600290811061230657fe5b9060200190602002015187600681518110151561231f57fe5b6020908102909101810191909152604080518083018f9052600481830152600080516020612988833981519152606082015281516049818303018152606990910191829052805190928291908401908083835b602083106123915780518252601f199092019160209182019101612372565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390208660078151811015156123cd57fe5b602090810290910101528051819060039081106123e657fe5b906020019060200201518760078151811015156123ff57fe5b6020908102909101810191909152600354604080517fedf87aff00000000000000000000000000000000000000000000000000000000815260048101918252895160448201528951600160a060020a039093169363edf87aff938b938d9390928392602483019260640191878101910280838360005b8381101561248d578181015183820152602001612475565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156124cc5781810151838201526020016124b4565b50505050905001945050505050602060405180830381600087803b1580156124f357600080fd5b505af1158015612507573d6000803e3d6000fd5b505050506040513d602081101561251d57600080fd5b5051151561252a57600080fd5b604080518d815260208101879052808201869052606081018d905260808101849052905133917f119326b1fd8ed92ff4f7d9da36e3caa7d39681c38d437053fe7e3965cf74aaf8919081900360a00190a2505050505050505050505050565b600354600090600160a060020a031615156125a357600080fd5b6003546040805160208082018690527f67656e650000000000000000000000000000000000000000000000000000000082840152825160248184030181526044909201928390528151600160a060020a039094169363898536919391829190840190808383602083106113cb5780518252601f1990920191602091820191016113ac565b600081612633846128dc565b81151561263c57fe5b069392505050565b600061265f826126538561130d565b9063ffffffff61296816565b60035460408051600160a060020a038781166c01000000000000000000000000026020808401919091527f6372797074616e7400000000000000000000000000000000000000000000000060348401528351808403601c018152603c90930193849052825195965093169363adb44a32939192918291908401908083835b602083106126fc5780518252601f1990920191602091820191016126dd565b51815160209384036101000a60001901801990921691161790526040805192909401829003822063ffffffff881660e060020a028352600483015260248201899052925160448083019650939450929083900301905081600087803b15801561276457600080fd5b505af1158015612778573d6000803e3d6000fd5b505050506040513d602081101561278e57600080fd5b505060408051838152602081018390528151600160a060020a038616927fed28291c8ece55af2a3498316da394241d9026fd7256843843d9f4fa0a56f1e0928290030190a292915050565b60006127eb838363ffffffff61297516565b905060008111156106db57604051339082156108fc029083906000818181858888f19350505050158015612823573d6000803e3d6000fd5b506040805183815260208101859052808201839052905133917fb7048c2ad36aadee977bb16b4543a18866044d27a2ca753e1c7dbcfa7d7a6962919081900360600190a2505050565b600160a060020a038116151561288157600080fd5b60008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360008054600160a060020a031916600160a060020a0392909216919091179055565b604080516000194301406020808301919091528183018490528251808303840181526060909201928390528151600093918291908401908083835b602083106129365780518252601f199092019160209182019101612917565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b81810182811015610d9b57fe5b60008282111561298157fe5b50900390560070617274426f6e75730000000000000000000000000000000000000000000000a165627a7a72305820244b2abe0685bb21da4e2a3b5e7ea932b932639265adafc4d71843bfbd72a67d00290000000000000000000000002248bfa3babbf53fdc058167584a642d13eebfed000000000000000000000000ecd6b4a2f82b0c9fb283a4a8a1ef5adf555f794b000000000000000000000000c58e24f5b05365aa21ded72111af939446d13b2e00000000000000000000000094bb8ffbd1631041ceadc27bf0da9b4b0353f141000000000000000000000000bf9ce4aae144b6793472161e2f74b2ddc8e5ddd3

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

0000000000000000000000002248bfa3babbf53fdc058167584a642d13eebfed000000000000000000000000ecd6b4a2f82b0c9fb283a4a8a1ef5adf555f794b000000000000000000000000c58e24f5b05365aa21ded72111af939446d13b2e00000000000000000000000094bb8ffbd1631041ceadc27bf0da9b4b0353f141000000000000000000000000bf9ce4aae144b6793472161e2f74b2ddc8e5ddd3

-----Decoded View---------------
Arg [0] : _genesisCrabAddress (address): 0x2248bFA3bABbF53fDC058167584a642D13EEbfED
Arg [1] : _cryptantCrabTokenAddress (address): 0xECd6b4A2f82b0c9FB283A4a8a1ef5ADf555f794b
Arg [2] : _cryptantCrabStorageAddress (address): 0xc58e24f5B05365Aa21DED72111af939446D13B2e
Arg [3] : _transmuterAddress (address): 0x94Bb8Ffbd1631041cEadc27bF0da9B4b0353F141
Arg [4] : _prizePoolAddress (address): 0xBf9Ce4aAe144b6793472161e2F74B2ddC8e5ddd3

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000002248bfa3babbf53fdc058167584a642d13eebfed
Arg [1] : 000000000000000000000000ecd6b4a2f82b0c9fb283a4a8a1ef5adf555f794b
Arg [2] : 000000000000000000000000c58e24f5b05365aa21ded72111af939446d13b2e
Arg [3] : 00000000000000000000000094bb8ffbd1631041ceadc27bf0da9b4b0353f141
Arg [4] : 000000000000000000000000bf9ce4aae144b6793472161e2f74b2ddc8e5ddd3


Swarm Source

bzzr://244b2abe0685bb21da4e2a3b5e7ea932b932639265adafc4d71843bfbd72a67d
Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

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.