ETH Price: $2,068.05 (-0.12%)

Transaction Decoder

Block:
5519751 at Apr-28-2018 09:04:46 AM +UTC
Transaction Fee:
0.000638405 ETH $1.32
Gas Used:
127,681 Gas / 5 Gwei

Emitted Events:

70 EtheremonTrade.EventPlaceSellOrder( seller=[Sender] 0xbdbfe5f4d8775472b0398f88fc378ec2e5518558, objId=29317 )

Account State Difference:

  Address   Before After State Difference Code
0x4bA72F0F...d0fE47030
(Etheremon: Trade 1)
(Ethpool 2)
217.232260537689129294 Eth217.232898942689129294 Eth0.000638405
0xbDbfE5F4...2E5518558
0.089327591764232288 Eth
Nonce: 2271
0.088689186764232288 Eth
Nonce: 2272
0.000638405

Execution Trace

EtheremonTrade.placeSellOrder( _objId=29317, _price=59000000000000000 )
  • Etheremon: Contract 2.35f097f3( )
    • EtheremonData.getMonsterObj( _objId=29317 ) => ( objId=29317, classId=79, trainer=0xbDbfE5F4D8775472b0398F88FC378ec2E5518558, exp=908, createIndex=61, lastClaimIndex=61, createTime=1522589547 )
    • EtheremonBattle.isOnBattle( _objId=29317 ) => ( False )
      • EtheremonData.getMonsterObj( _objId=29317 ) => ( objId=29317, classId=79, trainer=0xbDbfE5F4D8775472b0398F88FC378ec2E5518558, exp=908, createIndex=61, lastClaimIndex=61, createTime=1522589547 )
      • EtheremonCastleBattle.getCastleBasicInfo( _owner=0xbDbfE5F4D8775472b0398F88FC378ec2E5518558 ) => ( 1964, 15, 5 )
      • EtheremonCastleBattle.isOnCastle( _castleId=1964, _objId=29317 ) => ( False )
      • EtheremonRankData.isOnBattle( _trainer=0xbDbfE5F4D8775472b0398F88FC378ec2E5518558, _objId=29317 ) => ( False )
      • EtheremonData.getMonsterObj( _objId=29317 ) => ( objId=29317, classId=79, trainer=0xbDbfE5F4D8775472b0398F88FC378ec2E5518558, exp=908, createIndex=61, lastClaimIndex=61, createTime=1522589547 )
        File 1 of 5: EtheremonTrade
        pragma solidity ^0.4.16;
        
        // copyright contact@Etheremon.com
        
        contract SafeMath {
        
            /* function assert(bool assertion) internal { */
            /*   if (!assertion) { */
            /*     throw; */
            /*   } */
            /* }      // assert no longer needed once solidity is on 0.4.10 */
        
            function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x + y;
              assert((z >= x) && (z >= y));
              return z;
            }
        
            function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
              assert(x >= y);
              uint256 z = x - y;
              return z;
            }
        
            function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x * y;
              assert((x == 0)||(z/x == y));
              return z;
            }
        
        }
        
        contract BasicAccessControl {
            address public owner;
            // address[] public moderators;
            uint16 public totalModerators = 0;
            mapping (address => bool) public moderators;
            bool public isMaintaining = true;
        
            function BasicAccessControl() public {
                owner = msg.sender;
            }
        
            modifier onlyOwner {
                require(msg.sender == owner);
                _;
            }
        
            modifier onlyModerators() {
                require(msg.sender == owner || moderators[msg.sender] == true);
                _;
            }
        
            modifier isActive {
                require(!isMaintaining);
                _;
            }
        
            function ChangeOwner(address _newOwner) onlyOwner public {
                if (_newOwner != address(0)) {
                    owner = _newOwner;
                }
            }
        
        
            function AddModerator(address _newModerator) onlyOwner public {
                if (moderators[_newModerator] == false) {
                    moderators[_newModerator] = true;
                    totalModerators += 1;
                }
            }
            
            function RemoveModerator(address _oldModerator) onlyOwner public {
                if (moderators[_oldModerator] == true) {
                    moderators[_oldModerator] = false;
                    totalModerators -= 1;
                }
            }
        
            function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
                isMaintaining = _isMaintaining;
            }
        }
        
        contract EtheremonEnum {
        
            enum ResultCode {
                SUCCESS,
                ERROR_CLASS_NOT_FOUND,
                ERROR_LOW_BALANCE,
                ERROR_SEND_FAIL,
                ERROR_NOT_TRAINER,
                ERROR_NOT_ENOUGH_MONEY,
                ERROR_INVALID_AMOUNT,
                ERROR_OBJ_NOT_FOUND,
                ERROR_OBJ_INVALID_OWNERSHIP
            }
            
            enum ArrayType {
                CLASS_TYPE,
                STAT_STEP,
                STAT_START,
                STAT_BASE,
                OBJ_SKILL
            }
        }
        
        contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
            
            uint64 public totalMonster;
            uint32 public totalClass;
            
            // write
            function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint);
            function removeElementOfArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint);
            function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32);
            function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64);
            function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public;
            function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
            function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
            function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256 amount);
            function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount);
            function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode);
            function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
            function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
            function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public;
            
            // read
            function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
            function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
            function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
            function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
            function getMonsterName(uint64 _objId) constant public returns(string name);
            function getExtraBalance(address _trainer) constant public returns(uint256);
            function getMonsterDexSize(address _trainer) constant public returns(uint);
            function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
            function getExpectedBalance(address _trainer) constant public returns(uint256);
            function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
        }
        
        interface EtheremonBattleInterface {
            function isOnBattle(uint64 _objId) constant external returns(bool) ;
            function getMonsterCP(uint64 _objId) constant external returns(uint64);
        }
        
        contract EtheremonTrade is EtheremonEnum, BasicAccessControl, SafeMath {
            
            uint8 constant public GEN0_NO = 24;
        
            struct MonsterClassAcc {
                uint32 classId;
                uint256 price;
                uint256 returnPrice;
                uint32 total;
                bool catchable;
            }
        
            struct MonsterObjAcc {
                uint64 monsterId;
                uint32 classId;
                address trainer;
                string name;
                uint32 exp;
                uint32 createIndex;
                uint32 lastClaimIndex;
                uint createTime;
            }
            
            // Gen0 has return price & no longer can be caught when this contract is deployed
            struct Gen0Config {
                uint32 classId;
                uint256 originalPrice;
                uint256 returnPrice;
                uint32 total; // total caught (not count those from eggs)
            }
            
            struct BorrowItem {
                uint index;
                address owner;
                address borrower;
                uint256 price;
                bool lent;
                uint releaseTime;
            }
            
            struct SellingItem {
                uint index;
                uint256 price;
            }
            
            struct SoldItem {
                uint64 objId;
                uint256 price;
                uint time;
            }
            
            // data contract
            address public dataContract;
            address public battleContract;
            mapping(uint32 => Gen0Config) public gen0Config;
            
            // for selling
            mapping(uint64 => SellingItem) public sellingDict;
            uint32 public totalSellingItem;
            uint64[] public sellingList;
            
            // for borrowing
            mapping(uint64 => BorrowItem) public borrowingDict;
            uint32 public totalBorrowingItem;
            uint64[] public borrowingList;
            
            mapping(address => uint64[]) public lendingList;
            mapping(address => SoldItem[]) public soldList;
            
            // trading fee
            uint16 public tradingFeePercentage = 1;
            uint8 public maxLendingItem = 10;
            
            modifier requireDataContract {
                require(dataContract != address(0));
                _;
            }
            
            modifier requireBattleContract {
                require(battleContract != address(0));
                _;
            }
            
            // event
            event EventPlaceSellOrder(address indexed seller, uint64 objId);
            event EventBuyItem(address indexed buyer, uint64 objId);
            event EventOfferBorrowingItem(address indexed lender, uint64 objId);
            event EventAcceptBorrowItem(address indexed borrower, uint64 objId);
            event EventGetBackItem(address indexed owner, uint64 objId);
            event EventFreeTransferItem(address indexed sender, address indexed receiver, uint64 objId);
            event EventRelease(address indexed trainer, uint64 objId);
            
            // constructor
            function EtheremonTrade(address _dataContract, address _battleContract) public {
                dataContract = _dataContract;
                battleContract = _battleContract;
            }
            
             // admin & moderators
            function setOriginalPriceGen0() onlyModerators public {
                gen0Config[1] = Gen0Config(1, 0.3 ether, 0.003 ether, 374);
                gen0Config[2] = Gen0Config(2, 0.3 ether, 0.003 ether, 408);
                gen0Config[3] = Gen0Config(3, 0.3 ether, 0.003 ether, 373);
                gen0Config[4] = Gen0Config(4, 0.2 ether, 0.002 ether, 437);
                gen0Config[5] = Gen0Config(5, 0.1 ether, 0.001 ether, 497);
                gen0Config[6] = Gen0Config(6, 0.3 ether, 0.003 ether, 380); 
                gen0Config[7] = Gen0Config(7, 0.2 ether, 0.002 ether, 345);
                gen0Config[8] = Gen0Config(8, 0.1 ether, 0.001 ether, 518); 
                gen0Config[9] = Gen0Config(9, 0.1 ether, 0.001 ether, 447);
                gen0Config[10] = Gen0Config(10, 0.2 ether, 0.002 ether, 380); 
                gen0Config[11] = Gen0Config(11, 0.2 ether, 0.002 ether, 354);
                gen0Config[12] = Gen0Config(12, 0.2 ether, 0.002 ether, 346);
                gen0Config[13] = Gen0Config(13, 0.2 ether, 0.002 ether, 351); 
                gen0Config[14] = Gen0Config(14, 0.2 ether, 0.002 ether, 338);
                gen0Config[15] = Gen0Config(15, 0.2 ether, 0.002 ether, 341);
                gen0Config[16] = Gen0Config(16, 0.35 ether, 0.0035 ether, 384);
                gen0Config[17] = Gen0Config(17, 1 ether, 0.01 ether, 305); 
                gen0Config[18] = Gen0Config(18, 0.1 ether, 0.001 ether, 427);
                gen0Config[19] = Gen0Config(19, 1 ether, 0.01 ether, 304);
                gen0Config[20] = Gen0Config(20, 0.4 ether, 0.05 ether, 82);
                gen0Config[21] = Gen0Config(21, 1, 1, 123);
                gen0Config[22] = Gen0Config(22, 0.2 ether, 0.001 ether, 468);
                gen0Config[23] = Gen0Config(23, 0.5 ether, 0.0025 ether, 302);
                gen0Config[24] = Gen0Config(24, 1 ether, 0.005 ether, 195);
            }
            
            function setContract(address _dataContract, address _battleContract) onlyModerators public {
                dataContract = _dataContract;
                battleContract = _battleContract;
            }
            
            function updateConfig(uint16 _fee, uint8 _maxLendingItem) onlyModerators public {
                tradingFeePercentage = _fee;
                maxLendingItem = _maxLendingItem;
            }
            
            function withdrawEther(address _sendTo, uint _amount) onlyModerators public {
                // no user money is kept in this contract, only trasaction fee
                if (_amount > this.balance) {
                    revert();
                }
                _sendTo.transfer(_amount);
            }
            
            
            // helper
            function removeSellingItem(uint64 _itemId) private {
                SellingItem storage item = sellingDict[_itemId];
                if (item.index == 0)
                    return;
                
                if (item.index <= sellingList.length) {
                    // Move an existing element into the vacated key slot.
                    sellingDict[sellingList[sellingList.length-1]].index = item.index;
                    sellingList[item.index-1] = sellingList[sellingList.length-1];
                    sellingList.length -= 1;
                    delete sellingDict[_itemId];
                }
            }
            
            function addSellingItem(uint64 _itemId, uint256 _price) private {
                SellingItem storage item = sellingDict[_itemId];
                item.price = _price;
                
                if (item.index == 0) {
                    item.index = ++sellingList.length;
                    sellingList[item.index - 1] = _itemId;
                }
            }
        
            function removeBorrowingItem(uint64 _itemId) private {
                BorrowItem storage item = borrowingDict[_itemId];
                if (item.index == 0)
                    return;
                
                if (item.index <= borrowingList.length) {
                    // Move an existing element into the vacated key slot.
                    borrowingDict[borrowingList[borrowingList.length-1]].index = item.index;
                    borrowingList[item.index-1] = borrowingList[borrowingList.length-1];
                    borrowingList.length -= 1;
                    delete borrowingDict[_itemId];
                }
            }
        
            function addBorrowingItem(address _owner, uint64 _itemId, uint256 _price, uint _releaseTime) private {
                BorrowItem storage item = borrowingDict[_itemId];
                item.owner = _owner;
                item.borrower = address(0);
                item.price = _price;
                item.lent = false;
                item.releaseTime = _releaseTime;
                
                if (item.index == 0) {
                    item.index = ++borrowingList.length;
                    borrowingList[item.index - 1] = _itemId;
                }
            }
            
            function transferMonster(address _to, uint64 _objId) private {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
        
                MonsterObjAcc memory obj;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
        
                // clear balance for gen 0
                if (obj.classId <= GEN0_NO) {
                    Gen0Config storage gen0 = gen0Config[obj.classId];
                    if (gen0.classId == obj.classId) {
                        if (obj.lastClaimIndex < gen0.total) {
                            uint32 gap = uint32(safeSubtract(gen0.total, obj.lastClaimIndex));
                            if (gap > 0) {
                                data.addExtraBalance(obj.trainer, safeMult(gap, gen0.returnPrice));
                                // reset total (accept name is cleared :( )
                                data.setMonsterObj(obj.monsterId, " name me ", obj.exp, obj.createIndex, gen0.total);
                            }
                        }
                    }
                }
                
                // transfer owner
                data.removeMonsterIdMapping(obj.trainer, _objId);
                data.addMonsterIdMapping(_to, _objId);
            }
            
            function addItemLendingList(address _trainer, uint64 _objId) private {
                if (_trainer != address(0)) {
                    uint64[] storage objList = lendingList[_trainer];
                    for (uint index = 0; index < objList.length; index++) {
                        if (objList[index] == _objId) {
                            return;
                        }
                    }
                    objList.push(_objId);
                }
            }
            
            function removeItemLendingList(address _trainer, uint64 _objId) private {
                uint foundIndex = 0;
                uint64[] storage objList = lendingList[_trainer];
                for (; foundIndex < objList.length; foundIndex++) {
                    if (objList[foundIndex] == _objId) {
                        break;
                    }
                }
                if (foundIndex < objList.length) {
                    objList[foundIndex] = objList[objList.length-1];
                    delete objList[objList.length-1];
                    objList.length--;
                }
            }
            
            // public
            function placeSellOrder(uint64 _objId, uint256 _price) requireDataContract requireBattleContract isActive external {
                if (_price == 0)
                    revert();
                // not on borrowing
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index > 0)
                    revert();
                // not on battle 
                EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                if (battle.isOnBattle(_objId))
                    revert();
                
                // check ownership
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                
                if (obj.monsterId != _objId) {
                    revert();
                }
                
                if (obj.trainer != msg.sender) {
                    revert();
                }
                
                // on selling, then just update price
                if (sellingDict[_objId].index > 0){
                    sellingDict[_objId].price = _price;
                } else {
                    addSellingItem(_objId, _price);
                }
                EventPlaceSellOrder(msg.sender, _objId);
            }
            
            function removeSellOrder(uint64 _objId) requireDataContract requireBattleContract isActive external {
                if (sellingDict[_objId].index == 0)
                    revert();
                
                // check ownership
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                
                if (obj.monsterId != _objId) {
                    revert();
                }
                
                if (obj.trainer != msg.sender) {
                    revert();
                }
                
                removeSellingItem(_objId);
            }
            
            function buyItem(uint64 _objId) requireDataContract requireBattleContract isActive external payable {
                // check item is valid to sell 
                uint256 requestPrice = sellingDict[_objId].price;
                if (requestPrice == 0 || msg.value != requestPrice) {
                    revert();
                }
                
                // check obj
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                if (obj.monsterId != _objId) {
                    revert();
                }
                // can not buy from yourself
                if (obj.trainer == msg.sender) {
                    revert();
                }
                
                address oldTrainer = obj.trainer;
                uint256 fee = requestPrice * tradingFeePercentage / 100;
                removeSellingItem(_objId);
                transferMonster(msg.sender, _objId);
                oldTrainer.transfer(safeSubtract(requestPrice, fee));
                
                SoldItem memory soldItem = SoldItem(_objId, requestPrice, block.timestamp);
                soldList[oldTrainer].push(soldItem);
                EventBuyItem(msg.sender, _objId);
            }
            
            function offerBorrowingItem(uint64 _objId, uint256 _price, uint _releaseTime) requireDataContract requireBattleContract isActive external {
                // make sure it is not on sale 
                if (sellingDict[_objId].price > 0 || _price == 0)
                    revert();
                // not on lent
                BorrowItem storage item = borrowingDict[_objId];
                if (item.lent == true)
                    revert();
                // not on battle 
                EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                if (battle.isOnBattle(_objId))
                    revert();
                
                
                // check ownership
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                
                if (obj.monsterId != _objId) {
                    revert();
                }
                
                if (obj.trainer != msg.sender) {
                    revert();
                }
                
                if (item.index > 0) {
                    // update info 
                    item.price = _price;
                    item.releaseTime = _releaseTime;
                } else {
                    addBorrowingItem(msg.sender, _objId, _price, _releaseTime);
                }
                EventOfferBorrowingItem(msg.sender, _objId);
            }
            
            function removeBorrowingOfferItem(uint64 _objId) requireDataContract requireBattleContract isActive external {
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index == 0)
                    revert();
                
                if (item.owner != msg.sender)
                    revert();
                if (item.lent == true)
                    revert();
                
                removeBorrowingItem(_objId);
            }
            
            function borrowItem(uint64 _objId) requireDataContract requireBattleContract isActive external payable {
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index == 0)
                    revert();
                if (item.lent == true)
                    revert();
                uint256 itemPrice = item.price;
                if (itemPrice != msg.value)
                    revert();
                
        
                // check obj
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                if (obj.monsterId != _objId) {
                    revert();
                }
                // can not borrow from yourself
                if (obj.trainer == msg.sender) {
                    revert();
                }
                
                uint256 fee = itemPrice * tradingFeePercentage / 100;
                item.borrower = msg.sender;
                item.releaseTime += block.timestamp;
                item.lent = true;
                address oldOwner = obj.trainer;
                transferMonster(msg.sender, _objId);
                oldOwner.transfer(safeSubtract(itemPrice, fee));
                addItemLendingList(oldOwner, _objId);
                EventAcceptBorrowItem(msg.sender, _objId);
            }
            
            function getBackLendingItem(uint64 _objId) requireDataContract requireBattleContract isActive external {
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index == 0)
                    revert();
                if (item.lent == false)
                    revert();
                if (item.releaseTime > block.timestamp)
                    revert();
                
                if (msg.sender != item.owner)
                    revert();
                
                removeBorrowingItem(_objId);
                transferMonster(msg.sender, _objId);
                removeItemLendingList(msg.sender, _objId);
                EventGetBackItem(msg.sender, _objId);
            }
            
            function freeTransferItem(uint64 _objId, address _receiver) requireDataContract requireBattleContract external {
                // make sure it is not on sale 
                if (sellingDict[_objId].price > 0)
                    revert();
                // not on borrowing
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index > 0)
                    revert();
                // not on battle 
                EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                if (battle.isOnBattle(_objId))
                    revert();
                
                // check ownership
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                
                if (obj.monsterId != _objId) {
                    revert();
                }
                
                if (obj.trainer != msg.sender) {
                    revert();
                }
                
                transferMonster(_receiver, _objId);
                EventFreeTransferItem(msg.sender, _receiver, _objId);
            }
            
            function release(uint64 _objId) requireDataContract requireBattleContract external {
                // make sure it is not on sale 
                if (sellingDict[_objId].price > 0)
                    revert();
                // not on borrowing
                BorrowItem storage item = borrowingDict[_objId];
                if (item.index > 0)
                    revert();
                // not on battle 
                EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                if (battle.isOnBattle(_objId))
                    revert();
                
                // check ownership
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                
                // can not release gen 0
                if (obj.classId <= GEN0_NO) {
                    revert();
                }
                
                if (obj.monsterId != _objId) {
                    revert();
                }
                
                if (obj.trainer != msg.sender) {
                    revert();
                }
                
                data.removeMonsterIdMapping(msg.sender, _objId);
                EventRelease(msg.sender, _objId);
            }
            
            // read access
            
            function getBasicObjInfo(uint64 _objId) constant public returns(uint32, address, uint32, uint32){
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
                return (obj.classId, obj.trainer, obj.exp, obj.createIndex);
            }
            
            function getBasicObjInfoWithBp(uint64 _objId) constant public returns(uint32, uint32, uint32, uint64) {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
                EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                uint64 bp = battle.getMonsterCP(_objId);
                return (obj.classId, obj.exp, obj.createIndex, bp);
            }
            
            function getTotalSellingItem() constant external returns(uint) {
                return sellingList.length;
            }
        
            function getSellingItem(uint _index) constant external returns(uint64 objId, uint32 classId, uint32 exp, uint64 bp, address trainer, uint createIndex, uint256 price) {
                objId = sellingList[_index];
                if (objId > 0) {
                    (classId, trainer, exp, createIndex) = getBasicObjInfo(objId);
                    EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                    bp = battle.getMonsterCP(objId);
                    price = sellingDict[objId].price;
                }
            }
            
            function getSellingItemByObjId(uint64 _objId) constant external returns(uint32 classId, uint32 exp, uint64 bp, address trainer, uint createIndex, uint256 price) {
                price = sellingDict[_objId].price;
                if (price > 0) {
                    (classId, trainer, exp, createIndex) = getBasicObjInfo(_objId);
                    EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                    bp = battle.getMonsterCP(_objId);
                }
            }
        
            function getTotalBorrowingItem() constant external returns(uint) {
                return borrowingList.length;
            }
        
            function getBorrowingItem(uint _index) constant external returns(uint64 objId, address owner, address borrower, 
                uint256 price, bool lent, uint releaseTime, uint32 classId, uint32 exp, uint32 createIndex, uint64 bp) {
                objId = borrowingList[_index];
                BorrowItem storage item = borrowingDict[objId];
                owner = item.owner;
                borrower = item.borrower;
                price = item.price;
                lent = item.lent;
                releaseTime = item.releaseTime;
                
                (classId, exp, createIndex, bp) = getBasicObjInfoWithBp(objId);
            }
            
            function getBorrowingItemByObjId(uint64 _objId) constant external returns(uint index, address owner, address borrower, 
                uint256 price, bool lent, uint releaseTime, uint32 classId, uint32 exp, uint32 createIndex, uint64 bp) {
                BorrowItem storage item = borrowingDict[_objId];
                index = item.index;
                owner = item.owner;
                borrower = item.borrower;
                price = item.price;
                lent = item.lent;
                releaseTime = item.releaseTime;
                
                (classId, exp, createIndex, bp) = getBasicObjInfoWithBp(_objId);
            }
            
            function getSoldItemLength(address _trainer) constant external returns(uint) {
                return soldList[_trainer].length;
            }
            
            function getSoldItem(address _trainer, uint _index) constant external returns(uint64 objId, uint32 classId, uint32 exp, uint64 bp, address currentOwner, 
                uint createIndex, uint256 price, uint time) {
                if (_index > soldList[_trainer].length)
                    return;
                SoldItem memory soldItem = soldList[_trainer][_index];
                objId = soldItem.objId;
                price = soldItem.price;
                time = soldItem.time;
                if (objId > 0) {
                    (classId, currentOwner, exp, createIndex) = getBasicObjInfo(objId);
                    EtheremonBattleInterface battle = EtheremonBattleInterface(battleContract);
                    bp = battle.getMonsterCP(objId);
                }
            }
            
            function getLendingItemLength(address _trainer) constant external returns(uint) {
                return lendingList[_trainer].length;
            }
            
            function getLendingItemInfo(address _trainer, uint _index) constant external returns(uint64 objId, address owner, address borrower, 
                uint256 price, bool lent, uint releaseTime, uint32 classId, uint32 exp, uint32 createIndex, uint64 bp) {
                if (_index > lendingList[_trainer].length)
                    return;
                objId = lendingList[_trainer][_index];
                BorrowItem storage item = borrowingDict[objId];
                owner = item.owner;
                borrower = item.borrower;
                price = item.price;
                lent = item.lent;
                releaseTime = item.releaseTime;
                
                (classId, exp, createIndex, bp) = getBasicObjInfoWithBp(objId);
            }
            
            function getTradingInfo(uint64 _objId) constant external returns(uint256 sellingPrice, uint256 lendingPrice, bool lent, uint releaseTime) {
                sellingPrice = sellingDict[_objId].price;
                BorrowItem storage item = borrowingDict[_objId];
                lendingPrice = item.price;
                lent = item.lent;
                releaseTime = item.releaseTime;
            }
            
            function isOnTrading(uint64 _objId) constant external returns(bool) {
                return (sellingDict[_objId].price > 0 || borrowingDict[_objId].owner != address(0));
            }
        }

        File 2 of 5: EtheremonData
        pragma solidity ^0.4.16;
        
        // copyright contact@Etheremon.com
        
        contract SafeMath {
        
            /* function assert(bool assertion) internal { */
            /*   if (!assertion) { */
            /*     throw; */
            /*   } */
            /* }      // assert no longer needed once solidity is on 0.4.10 */
        
            function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x + y;
              assert((z >= x) && (z >= y));
              return z;
            }
        
            function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
              assert(x >= y);
              uint256 z = x - y;
              return z;
            }
        
            function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x * y;
              assert((x == 0)||(z/x == y));
              return z;
            }
        
        }
        
        contract BasicAccessControl {
            address public owner;
            address[] public moderators;
        
            function BasicAccessControl() public {
                owner = msg.sender;
            }
        
            modifier onlyOwner {
                require(msg.sender == owner);
                _;
            }
        
            modifier onlyModerators() {
                if (msg.sender != owner) {
                    bool found = false;
                    for (uint index = 0; index < moderators.length; index++) {
                        if (moderators[index] == msg.sender) {
                            found = true;
                            break;
                        }
                    }
                    require(found);
                }
                _;
            }
        
            function ChangeOwner(address _newOwner) onlyOwner public {
                if (_newOwner != address(0)) {
                    owner = _newOwner;
                }
            }
        
            function Kill() onlyOwner public {
                selfdestruct(owner);
            }
        
            function AddModerator(address _newModerator) onlyOwner public {
                if (_newModerator != address(0)) {
                    for (uint index = 0; index < moderators.length; index++) {
                        if (moderators[index] == _newModerator) {
                            return;
                        }
                    }
                    moderators.push(_newModerator);
                }
            }
            
            function RemoveModerator(address _oldModerator) onlyOwner public {
                uint foundIndex = 0;
                for (; foundIndex < moderators.length; foundIndex++) {
                    if (moderators[foundIndex] == _oldModerator) {
                        break;
                    }
                }
                if (foundIndex < moderators.length) {
                    moderators[foundIndex] = moderators[moderators.length-1];
                    delete moderators[moderators.length-1];
                    moderators.length--;
                }
            }
        }
        
        
        contract EtheremonEnum {
        
            enum ResultCode {
                SUCCESS,
                ERROR_CLASS_NOT_FOUND,
                ERROR_LOW_BALANCE,
                ERROR_SEND_FAIL,
                ERROR_NOT_TRAINER,
                ERROR_NOT_ENOUGH_MONEY,
                ERROR_INVALID_AMOUNT
            }
            
            enum ArrayType {
                CLASS_TYPE,
                STAT_STEP,
                STAT_START,
                STAT_BASE,
                OBJ_SKILL
            }
        }
        
        contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
            
            uint64 public totalMonster;
            uint32 public totalClass;
            
            // write
            function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint);
            function updateIndexOfArrayType(ArrayType _type, uint64 _id, uint _index, uint8 _value) onlyModerators public returns(uint);
            function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32);
            function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64);
            function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public;
            function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
            function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public;
            function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256 amount);
            function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount);
            function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode);
            function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
            function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256);
            function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public;
            
            // read
            function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
            function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
            function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
            function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
            function getMonsterName(uint64 _objId) constant public returns(string name);
            function getExtraBalance(address _trainer) constant public returns(uint256);
            function getMonsterDexSize(address _trainer) constant public returns(uint);
            function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
            function getExpectedBalance(address _trainer) constant public returns(uint256);
            function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
        }
        
        contract EtheremonData is EtheremonDataBase {
        
            struct MonsterClass {
                uint32 classId;
                uint8[] types;
                uint8[] statSteps;
                uint8[] statStarts;
                uint256 price;
                uint256 returnPrice;
                uint32 total;
                bool catchable;
            }
            
            struct MonsterObj {
                uint64 monsterId;
                uint32 classId;
                address trainer;
                string name;
                uint32 exp;
                uint8[] statBases;
                uint8[] skills;
                uint32 createIndex;
                uint32 lastClaimIndex;
                uint createTime;
            }
        
            mapping(uint32 => MonsterClass) public monsterClass;
            mapping(uint64 => MonsterObj) public monsterWorld;
            mapping(address => uint64[]) public trainerDex;
            mapping(address => uint256) public trainerExtraBalance;
            
            
            // write access
            function withdrawEther(address _sendTo, uint _amount) onlyOwner public returns(ResultCode) {
                if (_amount > this.balance) {
                    return ResultCode.ERROR_INVALID_AMOUNT;
                }
                
                _sendTo.transfer(_amount);
                return ResultCode.SUCCESS;
            }
            
            function addElementToArrayType(ArrayType _type, uint64 _id, uint8 _value) onlyModerators public returns(uint) {
                uint8[] storage array = monsterWorld[_id].statBases;
                if (_type == ArrayType.CLASS_TYPE) {
                    array = monsterClass[uint32(_id)].types;
                } else if (_type == ArrayType.STAT_STEP) {
                    array = monsterClass[uint32(_id)].statSteps;
                } else if (_type == ArrayType.STAT_START) {
                    array = monsterClass[uint32(_id)].statStarts;
                } else if (_type == ArrayType.OBJ_SKILL) {
                    array = monsterWorld[_id].skills;
                }
                array.push(_value);
                return array.length;
            }
            
            function updateIndexOfArrayType(ArrayType _type, uint64 _id, uint _index, uint8 _value) onlyModerators public returns(uint) {
                uint8[] storage array = monsterWorld[_id].statBases;
                if (_type == ArrayType.CLASS_TYPE) {
                    array = monsterClass[uint32(_id)].types;
                } else if (_type == ArrayType.STAT_STEP) {
                    array = monsterClass[uint32(_id)].statSteps;
                } else if (_type == ArrayType.STAT_START) {
                    array = monsterClass[uint32(_id)].statStarts;
                } else if (_type == ArrayType.OBJ_SKILL) {
                    array = monsterWorld[_id].skills;
                }
                if (_index < array.length) {
                    if (_value == 255) {
                        // consider as delete
                        for(uint i = _index; i < array.length - 1; i++) {
                            array[i] = array[i+1];
                        }
                        delete array[array.length-1];
                        array.length--;
                    } else {
                        array[_index] = _value;
                    }
                }
            }
            
            function setMonsterClass(uint32 _classId, uint256 _price, uint256 _returnPrice, bool _catchable) onlyModerators public returns(uint32) {
                MonsterClass storage class = monsterClass[_classId];
                if (class.classId == 0) {
                    totalClass += 1;
                }
                class.classId = _classId;
                class.price = _price;
                class.returnPrice = _returnPrice;
                class.catchable = _catchable;
                return totalClass;
            }
            
            function addMonsterObj(uint32 _classId, address _trainer, string _name) onlyModerators public returns(uint64) {
                MonsterClass storage class = monsterClass[_classId];
                if (class.classId == 0)
                    return 0;
                        
                // construct new monster
                totalMonster += 1;
                class.total += 1;
        
                MonsterObj storage obj = monsterWorld[totalMonster];
                obj.monsterId = totalMonster;
                obj.classId = _classId;
                obj.trainer = _trainer;
                obj.name = _name;
                obj.exp = 1;
                obj.createIndex = class.total;
                obj.lastClaimIndex = class.total;
                obj.createTime = now;
        
                // add to monsterdex
                addMonsterIdMapping(_trainer, obj.monsterId);
                return obj.monsterId;
            }
            
            function setMonsterObj(uint64 _objId, string _name, uint32 _exp, uint32 _createIndex, uint32 _lastClaimIndex) onlyModerators public {
                MonsterObj storage obj = monsterWorld[_objId];
                if (obj.monsterId == _objId) {
                    obj.name = _name;
                    obj.exp = _exp;
                    obj.createIndex = _createIndex;
                    obj.lastClaimIndex = _lastClaimIndex;
                }
            }
        
            function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
                MonsterObj storage obj = monsterWorld[_objId];
                if (obj.monsterId == _objId) {
                    obj.exp = uint32(safeAdd(obj.exp, amount));
                }
            }
        
            function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public {
                MonsterObj storage obj = monsterWorld[_objId];
                if (obj.monsterId == _objId) {
                    obj.exp = uint32(safeSubtract(obj.exp, amount));
                }
            }
        
            function removeMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public {
                uint foundIndex = 0;
                uint64[] storage objIdList = trainerDex[_trainer];
                for (; foundIndex < objIdList.length; foundIndex++) {
                    if (objIdList[foundIndex] == _monsterId) {
                        break;
                    }
                }
                if (foundIndex < objIdList.length) {
                    objIdList[foundIndex] = objIdList[objIdList.length-1];
                    delete objIdList[objIdList.length-1];
                    objIdList.length--;
                    MonsterObj storage monster = monsterWorld[_monsterId];
                    monster.trainer = 0;
                }
            }
            
            function addMonsterIdMapping(address _trainer, uint64 _monsterId) onlyModerators public {
                if (_trainer != address(0) && _monsterId > 0) {
                    uint64[] storage objIdList = trainerDex[_trainer];
                    for (uint i = 0; i < objIdList.length; i++) {
                        if (objIdList[i] == _monsterId) {
                            return;
                        }
                    }
                    objIdList.push(_monsterId);
                    MonsterObj storage monster = monsterWorld[_monsterId];
                    monster.trainer = _trainer;
                }
            }
            
            function clearMonsterReturnBalance(uint64 _monsterId) onlyModerators public returns(uint256) {
                MonsterObj storage monster = monsterWorld[_monsterId];
                MonsterClass storage class = monsterClass[monster.classId];
                if (monster.monsterId == 0 || class.classId == 0)
                    return 0;
                uint256 amount = 0;
                uint32 gap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
                if (gap > 0) {
                    monster.lastClaimIndex = class.total;
                    amount = safeMult(gap, class.returnPrice);
                    trainerExtraBalance[monster.trainer] = safeAdd(trainerExtraBalance[monster.trainer], amount);
                }
                return amount;
            }
            
            function collectAllReturnBalance(address _trainer) onlyModerators public returns(uint256 amount) {
                uint64[] storage objIdList = trainerDex[_trainer];
                for (uint i = 0; i < objIdList.length; i++) {
                    clearMonsterReturnBalance(objIdList[i]);
                }
                return trainerExtraBalance[_trainer];
            }
            
            function transferMonster(address _from, address _to, uint64 _monsterId) onlyModerators public returns(ResultCode) {
                MonsterObj storage monster = monsterWorld[_monsterId];
                if (monster.trainer != _from) {
                    return ResultCode.ERROR_NOT_TRAINER;
                }
                
                clearMonsterReturnBalance(_monsterId);
                
                removeMonsterIdMapping(_from, _monsterId);
                addMonsterIdMapping(_to, _monsterId);
                return ResultCode.SUCCESS;
            }
            
            function addExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256) {
                trainerExtraBalance[_trainer] = safeAdd(trainerExtraBalance[_trainer], _amount);
                return trainerExtraBalance[_trainer];
            }
            
            function deductExtraBalance(address _trainer, uint256 _amount) onlyModerators public returns(uint256) {
                trainerExtraBalance[_trainer] = safeSubtract(trainerExtraBalance[_trainer], _amount);
                return trainerExtraBalance[_trainer];
            }
            
            function setExtraBalance(address _trainer, uint256 _amount) onlyModerators public {
                trainerExtraBalance[_trainer] = _amount;
            }
            
            
            // public
            function () payable public {
                addExtraBalance(msg.sender, msg.value);
            }
        
            // read access
            function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint) {
                uint8[] storage array = monsterWorld[_id].statBases;
                if (_type == ArrayType.CLASS_TYPE) {
                    array = monsterClass[uint32(_id)].types;
                } else if (_type == ArrayType.STAT_STEP) {
                    array = monsterClass[uint32(_id)].statSteps;
                } else if (_type == ArrayType.STAT_START) {
                    array = monsterClass[uint32(_id)].statStarts;
                } else if (_type == ArrayType.OBJ_SKILL) {
                    array = monsterWorld[_id].skills;
                }
                return array.length;
            }
            
            function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8) {
                uint8[] storage array = monsterWorld[_id].statBases;
                if (_type == ArrayType.CLASS_TYPE) {
                    array = monsterClass[uint32(_id)].types;
                } else if (_type == ArrayType.STAT_STEP) {
                    array = monsterClass[uint32(_id)].statSteps;
                } else if (_type == ArrayType.STAT_START) {
                    array = monsterClass[uint32(_id)].statStarts;
                } else if (_type == ArrayType.OBJ_SKILL) {
                    array = monsterWorld[_id].skills;
                }
                if (_index >= array.length)
                    return 0;
                return array[_index];
            }
            
            
            function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable) {
                MonsterClass storage class = monsterClass[_classId];
                classId = class.classId;
                price = class.price;
                returnPrice = class.returnPrice;
                total = class.total;
                catchable = class.catchable;
            }
            
            function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime) {
                MonsterObj storage monster = monsterWorld[_objId];
                objId = monster.monsterId;
                classId = monster.classId;
                trainer = monster.trainer;
                exp = monster.exp;
                createIndex = monster.createIndex;
                lastClaimIndex = monster.lastClaimIndex;
                createTime = monster.createTime;
            }
            
            function getMonsterName(uint64 _objId) constant public returns(string name) {
                return monsterWorld[_objId].name;
            }
        
            function getExtraBalance(address _trainer) constant public returns(uint256) {
                return trainerExtraBalance[_trainer];
            }
            
            function getMonsterDexSize(address _trainer) constant public returns(uint) {
                return trainerDex[_trainer].length;
            }
            
            function getMonsterObjId(address _trainer, uint index) constant public returns(uint64) {
                if (index >= trainerDex[_trainer].length)
                    return 0;
                return trainerDex[_trainer][index];
            }
            
            function getExpectedBalance(address _trainer) constant public returns(uint256) {
                uint64[] storage objIdList = trainerDex[_trainer];
                uint256 monsterBalance = 0;
                for (uint i = 0; i < objIdList.length; i++) {
                    MonsterObj memory monster = monsterWorld[objIdList[i]];
                    MonsterClass storage class = monsterClass[monster.classId];
                    uint32 gap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
                    monsterBalance += safeMult(gap, class.returnPrice);
                }
                return monsterBalance;
            }
            
            function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total) {
                MonsterObj memory monster = monsterWorld[_objId];
                MonsterClass storage class = monsterClass[monster.classId];
                uint32 totalGap = uint32(safeSubtract(class.total, monster.createIndex));
                uint32 currentGap = uint32(safeSubtract(class.total, monster.lastClaimIndex));
                return (safeMult(currentGap, class.returnPrice), safeMult(totalGap, class.returnPrice));
            }
        
        }

        File 3 of 5: EtheremonBattle
        pragma solidity ^0.4.16;
        
        // copyright contact@Etheremon.com
        
        contract SafeMath {
        
            /* function assert(bool assertion) internal { */
            /*   if (!assertion) { */
            /*     throw; */
            /*   } */
            /* }      // assert no longer needed once solidity is on 0.4.10 */
        
            function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x + y;
              assert((z >= x) && (z >= y));
              return z;
            }
        
            function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
              assert(x >= y);
              uint256 z = x - y;
              return z;
            }
        
            function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x * y;
              assert((x == 0)||(z/x == y));
              return z;
            }
        
        }
        
        contract BasicAccessControl {
            address public owner;
            // address[] public moderators;
            uint16 public totalModerators = 0;
            mapping (address => bool) public moderators;
            bool public isMaintaining = true;
        
            function BasicAccessControl() public {
                owner = msg.sender;
            }
        
            modifier onlyOwner {
                require(msg.sender == owner);
                _;
            }
        
            modifier onlyModerators() {
                require(msg.sender == owner || moderators[msg.sender] == true);
                _;
            }
        
            modifier isActive {
                require(!isMaintaining);
                _;
            }
        
            function ChangeOwner(address _newOwner) onlyOwner public {
                if (_newOwner != address(0)) {
                    owner = _newOwner;
                }
            }
        
        
            function AddModerator(address _newModerator) onlyOwner public {
                if (moderators[_newModerator] == false) {
                    moderators[_newModerator] = true;
                    totalModerators += 1;
                }
            }
            
            function RemoveModerator(address _oldModerator) onlyOwner public {
                if (moderators[_oldModerator] == true) {
                    moderators[_oldModerator] = false;
                    totalModerators -= 1;
                }
            }
        
            function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
                isMaintaining = _isMaintaining;
            }
        }
        
        contract EtheremonEnum {
        
            enum ResultCode {
                SUCCESS,
                ERROR_CLASS_NOT_FOUND,
                ERROR_LOW_BALANCE,
                ERROR_SEND_FAIL,
                ERROR_NOT_TRAINER,
                ERROR_NOT_ENOUGH_MONEY,
                ERROR_INVALID_AMOUNT,
                ERROR_OBJ_NOT_FOUND,
                ERROR_OBJ_INVALID_OWNERSHIP
            }
            
            enum ArrayType {
                CLASS_TYPE,
                STAT_STEP,
                STAT_START,
                STAT_BASE,
                OBJ_SKILL
            }
        
            enum PropertyType {
                ANCESTOR,
                XFACTOR
            }
            
            enum BattleResult {
                CASTLE_WIN,
                CASTLE_LOSE,
                CASTLE_DESTROYED
            }
            
            enum CacheClassInfoType {
                CLASS_TYPE,
                CLASS_STEP,
                CLASS_ANCESTOR
            }
        }
        
        contract EtheremonDataBase is EtheremonEnum, BasicAccessControl, SafeMath {
            
            uint64 public totalMonster;
            uint32 public totalClass;
            
            // read
            function getSizeArrayType(ArrayType _type, uint64 _id) constant public returns(uint);
            function getElementInArrayType(ArrayType _type, uint64 _id, uint _index) constant public returns(uint8);
            function getMonsterClass(uint32 _classId) constant public returns(uint32 classId, uint256 price, uint256 returnPrice, uint32 total, bool catchable);
            function getMonsterObj(uint64 _objId) constant public returns(uint64 objId, uint32 classId, address trainer, uint32 exp, uint32 createIndex, uint32 lastClaimIndex, uint createTime);
            function getMonsterName(uint64 _objId) constant public returns(string name);
            function getExtraBalance(address _trainer) constant public returns(uint256);
            function getMonsterDexSize(address _trainer) constant public returns(uint);
            function getMonsterObjId(address _trainer, uint index) constant public returns(uint64);
            function getExpectedBalance(address _trainer) constant public returns(uint256);
            function getMonsterReturn(uint64 _objId) constant public returns(uint256 current, uint256 total);
        }
        
        interface EtheremonTradeInterface {
            function isOnTrading(uint64 _objId) constant external returns(bool);
        }
        
        interface EtheremonPaymentInterface {
            function giveBattleBonus(address _trainer, uint _amount) public; 
        }
        
        contract EtheremonGateway is EtheremonEnum, BasicAccessControl {
            // using for battle contract later
            function increaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            function decreaseMonsterExp(uint64 _objId, uint32 amount) onlyModerators public;
            
            // read 
            function isGason(uint64 _objId) constant external returns(bool);
            function getObjBattleInfo(uint64 _objId) constant external returns(uint32 classId, uint32 exp, bool isGason, 
                uint ancestorLength, uint xfactorsLength);
            function getClassPropertySize(uint32 _classId, PropertyType _type) constant external returns(uint);
            function getClassPropertyValue(uint32 _classId, PropertyType _type, uint index) constant external returns(uint32);
        }
        
        contract EtheremonCastleContract is EtheremonEnum, BasicAccessControl{
        
            uint32 public totalCastle = 0;
            uint64 public totalBattle = 0;
            
            function getCastleBasicInfo(address _owner) constant external returns(uint32, uint, uint32);
            function getCastleBasicInfoById(uint32 _castleId) constant external returns(uint, address, uint32);
            function countActiveCastle() constant external returns(uint);
            function getCastleObjInfo(uint32 _castleId) constant external returns(uint64, uint64, uint64, uint64, uint64, uint64);
            function getCastleStats(uint32 _castleId) constant external returns(string, address, uint32, uint32, uint32, uint);
            function isOnCastle(uint32 _castleId, uint64 _objId) constant external returns(bool);
            function getCastleWinLose(uint32 _castleId) constant external returns(uint32, uint32, uint32);
            function getTrainerBrick(address _trainer) constant external returns(uint32);
        
            function addCastle(address _trainer, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3, uint32 _brickNumber) 
                onlyModerators external returns(uint32 currentCastleId);
            function renameCastle(uint32 _castleId, string _name) onlyModerators external;
            function removeCastleFromActive(uint32 _castleId) onlyModerators external;
            function deductTrainerBrick(address _trainer, uint32 _deductAmount) onlyModerators external returns(bool);
            
            function addBattleLog(uint32 _castleId, address _attacker, 
                uint8 _ran1, uint8 _ran2, uint8 _ran3, uint8 _result, uint32 _castleExp1, uint32 _castleExp2, uint32 _castleExp3) onlyModerators external returns(uint64);
            function addBattleLogMonsterInfo(uint64 _battleId, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3, uint32 _exp1, uint32 _exp2, uint32 _exp3) onlyModerators external;
        }
        
        contract EtheremonBattle is EtheremonEnum, BasicAccessControl, SafeMath {
            uint8 constant public NO_MONSTER = 3;
            uint8 constant public STAT_COUNT = 6;
            uint8 constant public GEN0_NO = 24;
            
            struct MonsterClassAcc {
                uint32 classId;
                uint256 price;
                uint256 returnPrice;
                uint32 total;
                bool catchable;
            }
        
            struct MonsterObjAcc {
                uint64 monsterId;
                uint32 classId;
                address trainer;
                string name;
                uint32 exp;
                uint32 createIndex;
                uint32 lastClaimIndex;
                uint createTime;
            }
            
            struct BattleMonsterData {
                uint64 a1;
                uint64 a2;
                uint64 a3;
                uint64 s1;
                uint64 s2;
                uint64 s3;
            }
        
            struct SupporterData {
                uint32 classId1;
                bool isGason1;
                uint8 type1;
                uint32 classId2;
                bool isGason2;
                uint8 type2;
                uint32 classId3;
                bool isGason3;
                uint8 type3;
            }
        
            struct AttackData {
                uint64 aa;
                SupporterData asup;
                uint16 aAttackSupport;
                uint64 ba;
                SupporterData bsup;
                uint16 bAttackSupport;
                uint8 index;
            }
            
            struct MonsterBattleLog {
                uint64 objId;
                uint32 exp;
            }
            
            struct BattleLogData {
                address castleOwner;
                uint64 battleId;
                uint32 castleId;
                uint32[3] temp;
                uint castleIndex;
                uint8[6] monsterLevel;
                uint8[3] randoms;
                bool win;
                BattleResult result;
            }
            
            struct CacheClassInfo {
                uint8[] types;
                uint8[] steps;
                uint32[] ancestors;
            }
            
            struct CastleData {
                address trainer;
                string name;
                uint32 brickNumber;
                uint64 a1;
                uint64 a2;
                uint64 a3;
                uint64 s1;
                uint64 s2;
                uint64 s3;
            }
        
            // event
            event EventCreateCastle(address indexed owner, uint32 castleId);
            event EventAttackCastle(address indexed attacker, uint32 castleId, uint8 result);
            event EventRemoveCastle(uint32 indexed castleId);
            
            // linked smart contract
            address public worldContract;
            address public dataContract;
            address public tradeContract;
            address public castleContract;
            address public paymentContract;
            
            // global variable
            mapping(uint8 => uint8) typeAdvantages;
            mapping(uint32 => CacheClassInfo) cacheClasses;
            mapping(uint8 => uint32) levelExps;
            uint8 public ancestorBuffPercentage = 10;
            uint8 public gasonBuffPercentage = 10;
            uint8 public typeBuffPercentage = 20;
            uint8 public maxLevel = 100;
            uint16 public maxActiveCastle = 30;
            uint8 public maxRandomRound = 4;
            
            uint8 public winBrickReturn = 8;
            uint32 public castleMinBrick = 5;
            uint8 public castleExpAdjustment = 150; // percentage
            uint8 public attackerExpAdjustment = 50; // percentage
            uint8 public levelExpAdjustment = 3; // level
            uint8 public castleMaxLevelGap = 5;
            uint public brickETHPrice = 0.004 ether;
            uint8 public minHpDeducted = 10;
            uint public winTokenReward = 10 ** 8;
            
            uint256 public totalEarn = 0;
            uint256 public totalWithdraw = 0;
            
            address private lastAttacker = address(0x0);
            
            // modifier
            modifier requireDataContract {
                require(dataContract != address(0));
                _;
            }
            
            modifier requireTradeContract {
                require(tradeContract != address(0));
                _;
            }
            
            modifier requireCastleContract {
                require(castleContract != address(0));
                _;
            }
            
            modifier requireWorldContract {
                require(worldContract != address(0));
                _;
            }
            
            modifier requirePaymentContract {
                require(paymentContract != address(0));
                _;
            }
        
        
            function EtheremonBattle(address _dataContract, address _worldContract, address _tradeContract, address _castleContract, address _paymentContract) public {
                dataContract = _dataContract;
                worldContract = _worldContract;
                tradeContract = _tradeContract;
                castleContract = _castleContract;
                paymentContract = _paymentContract;
            }
            
             // admin & moderators
            function setTypeAdvantages() onlyModerators external {
                typeAdvantages[1] = 14;
                typeAdvantages[2] = 16;
                typeAdvantages[3] = 8;
                typeAdvantages[4] = 9;
                typeAdvantages[5] = 2;
                typeAdvantages[6] = 11;
                typeAdvantages[7] = 3;
                typeAdvantages[8] = 5;
                typeAdvantages[9] = 15;
                typeAdvantages[11] = 18;
                // skipp 10
                typeAdvantages[12] = 7;
                typeAdvantages[13] = 6;
                typeAdvantages[14] = 17;
                typeAdvantages[15] = 13;
                typeAdvantages[16] = 12;
                typeAdvantages[17] = 1;
                typeAdvantages[18] = 4;
            }
            
            function setTypeAdvantage(uint8 _type1, uint8 _type2) onlyModerators external {
                typeAdvantages[_type1] = _type2;
            }
            
            function setCacheClassInfo(uint32 _classId) onlyModerators requireDataContract requireWorldContract public {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                 EtheremonGateway gateway = EtheremonGateway(worldContract);
                uint i = 0;
                CacheClassInfo storage classInfo = cacheClasses[_classId];
        
                // add type
                i = data.getSizeArrayType(ArrayType.CLASS_TYPE, uint64(_classId));
                uint8[] memory aTypes = new uint8[](i);
                for(; i > 0 ; i--) {
                    aTypes[i-1] = data.getElementInArrayType(ArrayType.CLASS_TYPE, uint64(_classId), i-1);
                }
                classInfo.types = aTypes;
        
                // add steps
                i = data.getSizeArrayType(ArrayType.STAT_STEP, uint64(_classId));
                uint8[] memory steps = new uint8[](i);
                for(; i > 0 ; i--) {
                    steps[i-1] = data.getElementInArrayType(ArrayType.STAT_STEP, uint64(_classId), i-1);
                }
                classInfo.steps = steps;
                
                // add ancestor
                i = gateway.getClassPropertySize(_classId, PropertyType.ANCESTOR);
                uint32[] memory ancestors = new uint32[](i);
                for(; i > 0 ; i--) {
                    ancestors[i-1] = gateway.getClassPropertyValue(_classId, PropertyType.ANCESTOR, i-1);
                }
                classInfo.ancestors = ancestors;
            }
            
            function fastSetCacheClassInfo(uint32 _classId1, uint32 _classId2, uint32 _classId3, uint32 _classId4, uint32 _classId5, uint32 _classId6, uint32 _classId7, uint32 _classId8) 
                onlyModerators requireDataContract requireWorldContract public {
                setCacheClassInfo(_classId1);
                setCacheClassInfo(_classId2);
                setCacheClassInfo(_classId3);
                setCacheClassInfo(_classId4);
                setCacheClassInfo(_classId5);
                setCacheClassInfo(_classId6);
                setCacheClassInfo(_classId7);
                setCacheClassInfo(_classId8);
            }    
             
            function withdrawEther(address _sendTo, uint _amount) onlyModerators external {
                if (_amount > this.balance) {
                    revert();
                }
                uint256 validAmount = safeSubtract(totalEarn, totalWithdraw);
                if (_amount > validAmount) {
                    revert();
                }
                totalWithdraw += _amount;
                _sendTo.transfer(_amount);
            }
            
            function setContract(address _dataContract, address _worldContract, address _tradeContract, address _castleContract, address _paymentContract) onlyModerators external {
                dataContract = _dataContract;
                worldContract = _worldContract;
                tradeContract = _tradeContract;
                castleContract = _castleContract;
                paymentContract = _paymentContract;
            }
            
            function setConfig(uint8 _ancestorBuffPercentage, uint8 _gasonBuffPercentage, uint8 _typeBuffPercentage, 
                uint8 _maxLevel, uint8 _maxRandomRound, uint8 _minHpDeducted, uint _winTokenReward) onlyModerators external{
                ancestorBuffPercentage = _ancestorBuffPercentage;
                gasonBuffPercentage = _gasonBuffPercentage;
                typeBuffPercentage = _typeBuffPercentage;
                maxLevel = _maxLevel;
                maxRandomRound = _maxRandomRound;
                minHpDeducted = _minHpDeducted;
                winTokenReward = _winTokenReward;
            }
            
            function setCastleConfig(uint8 _castleMaxLevelGap, uint16 _maxActiveCastle, uint _brickETHPrice, uint8 _castleExpAdjustment, uint8 _attackerExpAdjustment, uint8 _levelExpAdjustment, uint32 _castleMinBrick) onlyModerators external {
                castleMaxLevelGap = _castleMaxLevelGap;
                maxActiveCastle = _maxActiveCastle;
                brickETHPrice = _brickETHPrice;
                castleExpAdjustment = _castleExpAdjustment;
                attackerExpAdjustment = _attackerExpAdjustment;
                levelExpAdjustment = _levelExpAdjustment;
                castleMinBrick = _castleMinBrick;
            }
            
            function genLevelExp() onlyModerators external {
                uint8 level = 1;
                uint32 requirement = 100;
                uint32 sum = requirement;
                while(level <= 100) {
                    levelExps[level] = sum;
                    level += 1;
                    requirement = (requirement * 11) / 10 + 5;
                    sum += requirement;
                }
            }
            
            // public 
            function getCacheClassSize(uint32 _classId) constant public returns(uint, uint, uint) {
                CacheClassInfo storage classInfo = cacheClasses[_classId];
                return (classInfo.types.length, classInfo.steps.length, classInfo.ancestors.length);
            }
            
            function getRandom(uint8 maxRan, uint8 index, address priAddress) constant public returns(uint8) {
                uint256 genNum = uint256(block.blockhash(block.number-1)) + uint256(priAddress);
                for (uint8 i = 0; i < index && i < 6; i ++) {
                    genNum /= 256;
                }
                return uint8(genNum % maxRan);
            }
            
            function getLevel(uint32 exp) view public returns (uint8) {
                uint8 minIndex = 1;
                uint8 maxIndex = 100;
                uint8 currentIndex;
             
                while (minIndex < maxIndex) {
                    currentIndex = (minIndex + maxIndex) / 2;
                    if (exp < levelExps[currentIndex])
                        maxIndex = currentIndex;
                    else
                        minIndex = currentIndex + 1;
                }
        
                return minIndex;
            }
            
            function getGainExp(uint8 level2, uint8 level, bool _win) constant public returns(uint32){
                uint8 halfLevel1 = level;
                if (level > level2 + 3) {
                    halfLevel1 = (level2 + 3) / 2;
                } else {
                    halfLevel1 = level / 2;
                }
                uint32 gainExp = 1;
                uint256 rate = (21 ** uint256(halfLevel1)) * 1000 / (20 ** uint256(halfLevel1));
                rate = rate * rate;
                if ((level > level2 + 3 && level2 + 3 > 2 * halfLevel1) || (level <= level2 + 3 && level > 2 * halfLevel1)) rate = rate * 21 / 20;
                if (_win) {
                    gainExp = uint32(30 * rate / 1000000);
                } else {
                    gainExp = uint32(10 * rate / 1000000);
                }
                
                if (level2 >= level + levelExpAdjustment) {
                    gainExp /= uint32(2) ** ((level2 - level) / levelExpAdjustment);
                }
                return gainExp;
            }
            
            function getMonsterLevel(uint64 _objId) constant external returns(uint32, uint8) {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
             
                return (obj.exp, getLevel(obj.exp));
            }
            
            function getMonsterCP(uint64 _objId) constant external returns(uint64) {
                uint16[6] memory stats;
                uint32 classId = 0;
                uint32 exp = 0;
                (classId, exp, stats) = getCurrentStats(_objId);
                
                uint256 total;
                for(uint i=0; i < STAT_COUNT; i+=1) {
                    total += stats[i];
                }
                return uint64(total/STAT_COUNT);
            }
            
            function isOnBattle(uint64 _objId) constant external returns(bool) {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                uint32 castleId;
                uint castleIndex = 0;
                uint256 price = 0;
                MonsterObjAcc memory obj;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
                (castleId, castleIndex, price) = castle.getCastleBasicInfo(obj.trainer);
                if (castleId > 0 && castleIndex > 0)
                    return castle.isOnCastle(castleId, _objId);
                return false;
            }
            
            function isValidOwner(uint64 _objId, address _owner) constant public returns(bool) {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                (obj.monsterId, obj.classId, obj.trainer, obj.exp, obj.createIndex, obj.lastClaimIndex, obj.createTime) = data.getMonsterObj(_objId);
                return (obj.trainer == _owner && obj.classId != 21);
            }
            
            function getObjExp(uint64 _objId) constant public returns(uint32, uint32) {
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                MonsterObjAcc memory obj;
                uint32 _ = 0;
                (_objId, obj.classId, obj.trainer, obj.exp, _, _, obj.createTime) = data.getMonsterObj(_objId);
                return (obj.classId, obj.exp);
            }
            
            function getCurrentStats(uint64 _objId) constant public returns(uint32, uint8, uint16[6]){
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                uint16[6] memory stats;
                uint32 classId;
                uint32 exp;
                (classId, exp) = getObjExp(_objId);
                if (classId == 0)
                    return (classId, 0, stats);
                
                uint i = 0;
                uint8 level = getLevel(exp);
                for(i=0; i < STAT_COUNT; i+=1) {
                    stats[i] = data.getElementInArrayType(ArrayType.STAT_BASE, _objId, i);
                }
                for(i=0; i < cacheClasses[classId].steps.length; i++) {
                    stats[i] += uint16(safeMult(cacheClasses[classId].steps[i], level*3));
                }
                return (classId, level, stats);
            }
            
            function safeDeduct(uint16 a, uint16 b) pure private returns(uint16){
                if (a > b) {
                    return a - b;
                }
                return 0;
            }
            
            function calHpDeducted(uint16 _attack, uint16 _specialAttack, uint16 _defense, uint16 _specialDefense, bool _lucky) view public returns(uint16){
                if (_lucky) {
                    _attack = _attack * 13 / 10;
                    _specialAttack = _specialAttack * 13 / 10;
                }
                uint16 hpDeducted = safeDeduct(_attack, _defense * 3 /4);
                uint16 hpSpecialDeducted = safeDeduct(_specialAttack, _specialDefense* 3 / 4);
                if (hpDeducted < minHpDeducted && hpSpecialDeducted < minHpDeducted)
                    return minHpDeducted;
                if (hpDeducted > hpSpecialDeducted)
                    return hpDeducted;
                return hpSpecialDeducted;
            }
            
            function getAncestorBuff(uint32 _classId, SupporterData _support) constant private returns(uint16){
                // check ancestors
                uint i =0;
                uint8 countEffect = 0;
                uint ancestorSize = cacheClasses[_classId].ancestors.length;
                if (ancestorSize > 0) {
                    uint32 ancestorClass = 0;
                    for (i=0; i < ancestorSize; i ++) {
                        ancestorClass = cacheClasses[_classId].ancestors[i];
                        if (ancestorClass == _support.classId1 || ancestorClass == _support.classId2 || ancestorClass == _support.classId3) {
                            countEffect += 1;
                        }
                    }
                }
                return countEffect * ancestorBuffPercentage;
            }
            
            function getGasonSupport(uint32 _classId, SupporterData _sup) constant private returns(uint16 defenseSupport) {
                uint i = 0;
                uint8 classType = 0;
                defenseSupport = 0;
                for (i = 0; i < cacheClasses[_classId].types.length; i++) {
                    classType = cacheClasses[_classId].types[i];
                     if (_sup.isGason1) {
                        if (classType == _sup.type1) {
                            defenseSupport += gasonBuffPercentage;
                            continue;
                        }
                    }
                    if (_sup.isGason2) {
                        if (classType == _sup.type2) {
                            defenseSupport += gasonBuffPercentage;
                            continue;
                        }
                    }
                    if (_sup.isGason3) {
                        if (classType == _sup.type3) {
                            defenseSupport += gasonBuffPercentage;
                            continue;
                        }
                    }
                }
            }
            
            function getTypeSupport(uint32 _aClassId, uint32 _bClassId) constant private returns (uint16 aAttackSupport, uint16 bAttackSupport) {
                // check types 
                bool aHasAdvantage;
                bool bHasAdvantage;
                for (uint i = 0; i < cacheClasses[_aClassId].types.length; i++) {
                    for (uint j = 0; j < cacheClasses[_bClassId].types.length; j++) {
                        if (typeAdvantages[cacheClasses[_aClassId].types[i]] == cacheClasses[_bClassId].types[j]) {
                            aHasAdvantage = true;
                        }
                        if (typeAdvantages[cacheClasses[_bClassId].types[j]] == cacheClasses[_aClassId].types[i]) {
                            bHasAdvantage = true;
                        }
                    }
                }
                
                if (aHasAdvantage)
                    aAttackSupport += typeBuffPercentage;
                if (bHasAdvantage)
                    bAttackSupport += typeBuffPercentage;
            }
            
            function calculateBattleStats(AttackData att) constant private returns(uint8 aLevel, uint16[6] aStats, uint8 bLevel, uint16[6] bStats) {
                uint32 aClassId = 0;
                (aClassId, aLevel, aStats) = getCurrentStats(att.aa);
                uint32 bClassId = 0;
                (bClassId, bLevel, bStats) = getCurrentStats(att.ba);
                
                // check gasonsupport
                (att.aAttackSupport, att.bAttackSupport) = getTypeSupport(aClassId, bClassId);
                att.aAttackSupport += getAncestorBuff(aClassId, att.asup);
                att.bAttackSupport += getAncestorBuff(bClassId, att.bsup);
                
                uint16 aDefenseBuff = getGasonSupport(aClassId, att.asup);
                uint16 bDefenseBuff = getGasonSupport(bClassId, att.bsup);
                
                // add attack
                aStats[1] += aStats[1] * att.aAttackSupport / 100;
                aStats[3] += aStats[3] * att.aAttackSupport / 100;
                bStats[1] += bStats[1] * att.bAttackSupport / 100;
                bStats[3] += bStats[3] * att.bAttackSupport / 100;
                
                // add offense
                aStats[2] += aStats[2] * aDefenseBuff / 100;
                aStats[4] += aStats[4] * aDefenseBuff / 100;
                bStats[2] += bStats[2] * bDefenseBuff / 100;
                bStats[4] += bStats[4] * bDefenseBuff / 100;
                
            }
            
            function attack(AttackData att) constant private returns(uint8 aLevel, uint8 bLevel, uint8 ran, bool win) {
                uint16[6] memory aStats;
                uint16[6] memory bStats;
                (aLevel, aStats, bLevel, bStats) = calculateBattleStats(att);
                
                ran = getRandom(maxRandomRound+2, att.index, lastAttacker);
                uint16 round = 0;
                while (round < maxRandomRound && aStats[0] > 0 && bStats[0] > 0) {
                    if (aStats[5] > bStats[5]) {
                        if (round % 2 == 0) {
                            // a attack 
                            bStats[0] = safeDeduct(bStats[0], calHpDeducted(aStats[1], aStats[3], bStats[2], bStats[4], round==ran));
                        } else {
                            aStats[0] = safeDeduct(aStats[0], calHpDeducted(bStats[1], bStats[3], aStats[2], aStats[4], round==ran));
                        }
                        
                    } else {
                        if (round % 2 != 0) {
                            bStats[0] = safeDeduct(bStats[0], calHpDeducted(aStats[1], aStats[3], bStats[2], bStats[4], round==ran));
                        } else {
                            aStats[0] = safeDeduct(aStats[0], calHpDeducted(bStats[1], bStats[3], aStats[2], aStats[4], round==ran));
                        }
                    }
                    round+= 1;
                }
                
                win = aStats[0] >= bStats[0];
            }
            
            function updateCastle(uint32 _castleId, address _castleOwner, bool win) requireCastleContract private{
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                uint32 totalWin;
                uint32 totalLose;
                uint32 brickNumber;
                (totalWin, totalLose, brickNumber) = castle.getCastleWinLose(_castleId);
                EtheremonPaymentInterface payment = EtheremonPaymentInterface(paymentContract);
                
                // if castle win, ignore
                if (win) {
                    if (totalWin < brickNumber) {
                         payment.giveBattleBonus(_castleOwner, winTokenReward);
                    }
                } else {
                    if (totalWin/winBrickReturn > brickNumber) {
                        brickNumber = 2 * brickNumber;
                    } else {
                        brickNumber += totalWin/winBrickReturn;
                    }
                    if (brickNumber <= totalLose + 1) {
                        castle.removeCastleFromActive(_castleId);
                        // destroy
                    }
                    payment.giveBattleBonus(msg.sender, winTokenReward);
                }
            }
            
            function hasValidParam(address _trainer, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) constant public returns(bool) {
                if (_a1 == 0 || _a2 == 0 || _a3 == 0)
                    return false;
                if (_a1 == _a2 || _a1 == _a3 || _a1 == _s1 || _a1 == _s2 || _a1 == _s3)
                    return false;
                if (_a2 == _a3 || _a2 == _s1 || _a2 == _s2 || _a2 == _s3)
                    return false;
                if (_a3 == _s1 || _a3 == _s2 || _a3 == _s3)
                    return false;
                if (_s1 > 0 && (_s1 == _s2 || _s1 == _s3))
                    return false;
                if (_s2 > 0 && (_s2 == _s3))
                    return false;
                
                if (!isValidOwner(_a1, _trainer) || !isValidOwner(_a2, _trainer) || !isValidOwner(_a3, _trainer))
                    return false;
                if (_s1 > 0 && !isValidOwner(_s1, _trainer))
                    return false;
                if (_s2 > 0 && !isValidOwner(_s2, _trainer))
                    return false;
                if (_s3 > 0 && !isValidOwner(_s3, _trainer))
                    return false;
                return true;
            }
            
            function createCastleInternal(CastleData _castleData) private {
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                uint32 castleId;
                uint castleIndex = 0;
                uint32 numberBrick = 0;
                (castleId, castleIndex, numberBrick) = castle.getCastleBasicInfo(_castleData.trainer);
                if (castleId > 0 || castleIndex > 0)
                    revert();
        
                if (castle.countActiveCastle() >= uint(maxActiveCastle))
                    revert();
                castleId = castle.addCastle(_castleData.trainer, _castleData.name, _castleData.a1, _castleData.a2, _castleData.a3, 
                    _castleData.s1, _castleData.s2, _castleData.s3, _castleData.brickNumber);
                EventCreateCastle(_castleData.trainer, castleId);
            }
            
            // public
            
            function createCastle(string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) isActive requireDataContract 
                requireTradeContract requireCastleContract payable external {
                
                if (!hasValidParam(msg.sender, _a1, _a2, _a3, _s1, _s2, _s3))
                    revert();
                
                EtheremonTradeInterface trade = EtheremonTradeInterface(tradeContract);
                if (trade.isOnTrading(_a1) || trade.isOnTrading(_a2) || trade.isOnTrading(_a3) || 
                    trade.isOnTrading(_s1) || trade.isOnTrading(_s2) || trade.isOnTrading(_s3))
                    revert();
                
                uint32 numberBrick = uint32(msg.value / brickETHPrice);
                if (numberBrick < castleMinBrick) {
                    revert();
                }
                
                CastleData memory castleData;
                castleData.trainer = msg.sender;
                castleData.name = _name;
                castleData.brickNumber = numberBrick;
                castleData.a1 = _a1;
                castleData.a2 = _a2;
                castleData.a3 = _a3;
                castleData.s1 = _s1;
                castleData.s2 = _s2;
                castleData.s3 = _s3;
                createCastleInternal(castleData);
                totalEarn += msg.value;
            }
            
            function createCastleWithToken(address _trainer, uint32 _noBrick, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) isActive requireDataContract
                requireTradeContract requireCastleContract requirePaymentContract external {
                // only accept request from payment contract
                if (msg.sender != paymentContract)
                    revert();
            
                if (!hasValidParam(_trainer, _a1, _a2, _a3, _s1, _s2, _s3))
                    revert();
                
                EtheremonTradeInterface trade = EtheremonTradeInterface(tradeContract);
                if (trade.isOnTrading(_a1) || trade.isOnTrading(_a2) || trade.isOnTrading(_a3) || 
                    trade.isOnTrading(_s1) || trade.isOnTrading(_s2) || trade.isOnTrading(_s3))
                    revert();
                
                if (_noBrick < castleMinBrick) {
                    revert();
                }
                
                CastleData memory castleData;
                castleData.trainer = _trainer;
                castleData.name = _name;
                castleData.brickNumber = _noBrick;
                castleData.a1 = _a1;
                castleData.a2 = _a2;
                castleData.a3 = _a3;
                castleData.s1 = _s1;
                castleData.s2 = _s2;
                castleData.s3 = _s3;
                createCastleInternal(castleData);
            }
            
            function renameCastle(uint32 _castleId, string _name) isActive requireCastleContract external {
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                uint index;
                address owner;
                uint256 price;
                (index, owner, price) = castle.getCastleBasicInfoById(_castleId);
                if (owner != msg.sender)
                    revert();
                castle.renameCastle(_castleId, _name);
            }
            
            function removeCastle(uint32 _castleId) isActive requireCastleContract external {
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                uint index;
                address owner;
                uint256 price;
                (index, owner, price) = castle.getCastleBasicInfoById(_castleId);
                if (owner != msg.sender)
                    revert();
                if (index > 0) {
                    castle.removeCastleFromActive(_castleId);
                }
                EventRemoveCastle(_castleId);
            }
            
            function getSupporterInfo(uint64 s1, uint64 s2, uint64 s3) constant public returns(SupporterData sData) {
                uint temp;
                uint32 __;
                EtheremonGateway gateway = EtheremonGateway(worldContract);
                if (s1 > 0)
                    (sData.classId1, __, sData.isGason1, temp, temp) = gateway.getObjBattleInfo(s1);
                if (s2 > 0)
                    (sData.classId2, __, sData.isGason2, temp, temp) = gateway.getObjBattleInfo(s2);
                if (s3 > 0)
                    (sData.classId3, __, sData.isGason3, temp, temp) = gateway.getObjBattleInfo(s3);
        
                EtheremonDataBase data = EtheremonDataBase(dataContract);
                if (sData.isGason1) {
                    sData.type1 = data.getElementInArrayType(ArrayType.CLASS_TYPE, uint64(sData.classId1), 0);
                }
                
                if (sData.isGason2) {
                    sData.type2 = data.getElementInArrayType(ArrayType.CLASS_TYPE, uint64(sData.classId2), 0);
                }
                
                if (sData.isGason3) {
                    sData.type3 = data.getElementInArrayType(ArrayType.CLASS_TYPE, uint64(sData.classId3), 0);
                }
            }
            
            function attackCastle(uint32 _castleId, uint64 _aa1, uint64 _aa2, uint64 _aa3, uint64 _as1, uint64 _as2, uint64 _as3) isActive requireDataContract 
                requireTradeContract requireCastleContract requirePaymentContract external {
                if (!hasValidParam(msg.sender, _aa1, _aa2, _aa3, _as1, _as2, _as3))
                    revert();
                
                EtheremonCastleContract castle = EtheremonCastleContract(castleContract);
                BattleLogData memory log;
                (log.castleIndex, log.castleOwner, log.temp[0]) = castle.getCastleBasicInfoById(_castleId);
                if (log.castleIndex == 0 || log.castleOwner == msg.sender)
                    revert();
                
                EtheremonGateway gateway = EtheremonGateway(worldContract);
                BattleMonsterData memory b;
                (b.a1, b.a2, b.a3, b.s1, b.s2, b.s3) = castle.getCastleObjInfo(_castleId);
                lastAttacker = msg.sender;
        
                // init data
                uint8 countWin = 0;
                AttackData memory att;
                att.asup = getSupporterInfo(b.s1, b.s2, b.s3);
                att.bsup = getSupporterInfo(_as1, _as2, _as3);
                
                att.index = 0;
                att.aa = b.a1;
                att.ba = _aa1;
                (log.monsterLevel[0], log.monsterLevel[3], log.randoms[0], log.win) = attack(att);
                gateway.increaseMonsterExp(att.aa, getGainExp(log.monsterLevel[0], log.monsterLevel[3], log.win)*castleExpAdjustment/100);
                gateway.increaseMonsterExp(att.ba, getGainExp(log.monsterLevel[3], log.monsterLevel[0], !log.win)*attackerExpAdjustment/100);
                if (log.win)
                    countWin += 1;
                
                
                att.index = 1;
                att.aa = b.a2;
                att.ba = _aa2;
                (log.monsterLevel[1], log.monsterLevel[4], log.randoms[1], log.win) = attack(att);
                gateway.increaseMonsterExp(att.aa, getGainExp(log.monsterLevel[1], log.monsterLevel[4], log.win)*castleExpAdjustment/100);
                gateway.increaseMonsterExp(att.ba, getGainExp(log.monsterLevel[4], log.monsterLevel[1], !log.win)*attackerExpAdjustment/100);
                if (log.win)
                    countWin += 1;   
        
                att.index = 2;
                att.aa = b.a3;
                att.ba = _aa3;
                (log.monsterLevel[2], log.monsterLevel[5], log.randoms[2], log.win) = attack(att);
                gateway.increaseMonsterExp(att.aa, getGainExp(log.monsterLevel[2], log.monsterLevel[5], log.win)*castleExpAdjustment/100);
                gateway.increaseMonsterExp(att.ba, getGainExp(log.monsterLevel[5], log.monsterLevel[2], !log.win)*attackerExpAdjustment/100);
                if (log.win)
                    countWin += 1; 
                
                if ((log.monsterLevel[0] + log.monsterLevel[1] + log.monsterLevel[2])/3 + castleMaxLevelGap < (log.monsterLevel[3] + log.monsterLevel[4] + log.monsterLevel[5])/3)
                    revert();
                
                updateCastle(_castleId, log.castleOwner, countWin>1);
                if (countWin>1) {
                    log.result = BattleResult.CASTLE_WIN;
                } else {
                    log.result = BattleResult.CASTLE_LOSE;
                }
                
                log.temp[0] = levelExps[log.monsterLevel[0]]-1;
                log.temp[1] = levelExps[log.monsterLevel[1]]-1;
                log.temp[2] = levelExps[log.monsterLevel[2]]-1;
                log.battleId = castle.addBattleLog(_castleId, msg.sender, log.randoms[0], log.randoms[1], log.randoms[2], 
                    uint8(log.result), log.temp[0], log.temp[1], log.temp[2]);
                
                log.temp[0] = levelExps[log.monsterLevel[3]]-1;
                log.temp[1] = levelExps[log.monsterLevel[4]]-1;
                log.temp[2] = levelExps[log.monsterLevel[5]]-1;
                castle.addBattleLogMonsterInfo(log.battleId, _aa1, _aa2, _aa3, _as1, _as2, _as3, log.temp[0], log.temp[1], log.temp[2]);
            
                EventAttackCastle(msg.sender, _castleId, uint8(log.result));
            }
            
        }

        File 4 of 5: EtheremonCastleBattle
        pragma solidity ^0.4.16;
        
        // copyright contact@Etheremon.com
        
        contract SafeMath {
        
            /* function assert(bool assertion) internal { */
            /*   if (!assertion) { */
            /*     throw; */
            /*   } */
            /* }      // assert no longer needed once solidity is on 0.4.10 */
        
            function safeAdd(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x + y;
              assert((z >= x) && (z >= y));
              return z;
            }
        
            function safeSubtract(uint256 x, uint256 y) pure internal returns(uint256) {
              assert(x >= y);
              uint256 z = x - y;
              return z;
            }
        
            function safeMult(uint256 x, uint256 y) pure internal returns(uint256) {
              uint256 z = x * y;
              assert((x == 0)||(z/x == y));
              return z;
            }
        
        }
        
        contract BasicAccessControl {
            address public owner;
            // address[] public moderators;
            uint16 public totalModerators = 0;
            mapping (address => bool) public moderators;
            bool public isMaintaining = true;
        
            function BasicAccessControl() public {
                owner = msg.sender;
            }
        
            modifier onlyOwner {
                require(msg.sender == owner);
                _;
            }
        
            modifier onlyModerators() {
                require(moderators[msg.sender] == true);
                _;
            }
        
            modifier isActive {
                require(!isMaintaining);
                _;
            }
        
            function ChangeOwner(address _newOwner) onlyOwner public {
                if (_newOwner != address(0)) {
                    owner = _newOwner;
                }
            }
        
        
            function AddModerator(address _newModerator) onlyOwner public {
                if (moderators[_newModerator] == false) {
                    moderators[_newModerator] = true;
                    totalModerators += 1;
                }
            }
            
            function RemoveModerator(address _oldModerator) onlyOwner public {
                if (moderators[_oldModerator] == true) {
                    moderators[_oldModerator] = false;
                    totalModerators -= 1;
                }
            }
        
            function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
                isMaintaining = _isMaintaining;
            }
        }
        
        contract EtheremonEnum {
        
            enum ResultCode {
                SUCCESS,
                ERROR_CLASS_NOT_FOUND,
                ERROR_LOW_BALANCE,
                ERROR_SEND_FAIL,
                ERROR_NOT_TRAINER,
                ERROR_NOT_ENOUGH_MONEY,
                ERROR_INVALID_AMOUNT
            }
            
            enum ArrayType {
                CLASS_TYPE,
                STAT_STEP,
                STAT_START,
                STAT_BASE,
                OBJ_SKILL
            }
        }
        
        
        contract EtheremonCastleBattle is EtheremonEnum, BasicAccessControl, SafeMath {
            uint8 constant public NO_BATTLE_LOG = 4;
            
            struct CastleData {
                uint index; // in active castle if > 0
                string name;
                address owner;
                uint32 totalWin;
                uint32 totalLose;
                uint64[6] monsters; // 3 attackers, 3 supporters
                uint64[4] battleList;
                uint32 brickNumber;
                uint createTime;
            }
            
            struct BattleDataLog {
                uint32 castleId;
                address attacker;
                uint32[3] castleExps; // 3 attackers
                uint64[6] attackerObjIds;
                uint32[3] attackerExps;
                uint8[3] randoms;
                uint8 result;
            }
            
            struct TrainerBattleLog {
                uint32 lastCastle;
                uint32 totalWin;
                uint32 totalLose;
                uint64[4] battleList;
                uint32 totalBrick;
            }
            
            mapping(uint64 => BattleDataLog) battles;
            mapping(address => uint32) trainerCastle;
            mapping(address => TrainerBattleLog) trannerBattleLog;
            mapping(uint32 => CastleData) castleData;
            uint32[] activeCastleList;
        
            uint32 public totalCastle = 0;
            uint64 public totalBattle = 0;
            
            // only moderators
            /*
            TO AVOID ANY BUGS, WE ALLOW MODERATORS TO HAVE PERMISSION TO ALL THESE FUNCTIONS AND UPDATE THEM IN EARLY BETA STAGE.
            AFTER THE SYSTEM IS STABLE, WE WILL REMOVE OWNER OF THIS SMART CONTRACT AND ONLY KEEP ONE MODERATOR WHICH IS ETHEREMON BATTLE CONTRACT.
            HENCE, THE DECENTRALIZED ATTRIBUTION IS GUARANTEED.
            */
            
            function addCastle(address _trainer, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3, uint32 _brickNumber) onlyModerators external returns(uint32 currentCastleId){
                currentCastleId = trainerCastle[_trainer];
                if (currentCastleId > 0)
                    return currentCastleId;
        
                totalCastle += 1;
                currentCastleId = totalCastle;
                CastleData storage castle = castleData[currentCastleId];
                castle.name = _name;
                castle.owner = _trainer;
                castle.monsters[0] = _a1;
                castle.monsters[1] = _a2;
                castle.monsters[2] = _a3;
                castle.monsters[3] = _s1;
                castle.monsters[4] = _s2;
                castle.monsters[5] = _s3;
                castle.brickNumber = _brickNumber;
                castle.createTime = now;
                
                castle.index = ++activeCastleList.length;
                activeCastleList[castle.index-1] = currentCastleId;
                // mark sender
                trainerCastle[_trainer] = currentCastleId;
            }
            
            function renameCastle(uint32 _castleId, string _name) onlyModerators external {
                CastleData storage castle = castleData[_castleId];
                castle.name = _name;
            }
            
            function removeCastleFromActive(uint32 _castleId) onlyModerators external {
                CastleData storage castle = castleData[_castleId];
                if (castle.index == 0)
                    return;
                
                trainerCastle[castle.owner] = 0;
                if (castle.index <= activeCastleList.length) {
                    // Move an existing element into the vacated key slot.
                    castleData[activeCastleList[activeCastleList.length-1]].index = castle.index;
                    activeCastleList[castle.index-1] = activeCastleList[activeCastleList.length-1];
                    activeCastleList.length -= 1;
                    castle.index = 0;
                }
                
                trannerBattleLog[castle.owner].lastCastle = _castleId;
            }
            
            function addBattleLog(uint32 _castleId, address _attacker, 
                uint8 _ran1, uint8 _ran2, uint8 _ran3, uint8 _result, uint32 _castleExp1, uint32 _castleExp2, uint32 _castleExp3) onlyModerators external returns(uint64) {
                totalBattle += 1;
                BattleDataLog storage battleLog = battles[totalBattle];
                battleLog.castleId = _castleId;
                battleLog.attacker = _attacker;
                battleLog.randoms[0] = _ran1;
                battleLog.randoms[1] = _ran2;
                battleLog.randoms[2] = _ran3;
                battleLog.result = _result;
                battleLog.castleExps[0] = _castleExp1;
                battleLog.castleExps[1] = _castleExp2;
                battleLog.castleExps[2] = _castleExp3;
                
                // 
                CastleData storage castle = castleData[_castleId];
                TrainerBattleLog storage trainerLog = trannerBattleLog[_attacker];
                /*
                CASTLE_WIN = 0 
                CASTLE_LOSE = 1 
                CASTLE_DESTROYED= 2
                */
                if (_result == 0) { // win
                    castle.totalWin += 1;
                    trainerLog.totalLose += 1;              
                } else {
                    castle.totalLose += 1;
                    trainerLog.totalWin += 1;
                    if (_result == 2) { // destroy
                        trainerLog.totalBrick += castle.brickNumber / 2;
                    }
                }
        
                castle.battleList[(castle.totalLose + castle.totalWin - 1)%NO_BATTLE_LOG] = totalBattle;
                trainerLog.battleList[(trainerLog.totalWin + trainerLog.totalLose - 1)%NO_BATTLE_LOG] = totalBattle;
                
                return totalBattle;
            }
            
            function addBattleLogMonsterInfo(uint64 _battleId, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3, uint32 _exp1, uint32 _exp2, uint32 _exp3) onlyModerators external {
                BattleDataLog storage battleLog = battles[_battleId];
                battleLog.attackerObjIds[0] = _a1;
                battleLog.attackerObjIds[1] = _a2;
                battleLog.attackerObjIds[2] = _a3;
                battleLog.attackerObjIds[3] = _s1;
                battleLog.attackerObjIds[4] = _s2;
                battleLog.attackerObjIds[5] = _s3;
                
                battleLog.attackerExps[0] = _exp1;
                battleLog.attackerExps[1] = _exp2;
                battleLog.attackerExps[2] = _exp3;
            }
            
            function deductTrainerBrick(address _trainer, uint32 _deductAmount) onlyModerators external returns(bool){
                TrainerBattleLog storage trainerLog = trannerBattleLog[_trainer];
                if (trainerLog.totalBrick < _deductAmount)
                    return false;
                trainerLog.totalBrick -= _deductAmount;
                return true;
            }
            
            // read access 
            function isCastleActive(uint32 _castleId) constant external returns(bool){
                CastleData storage castle = castleData[_castleId];
                return (castle.index > 0);
            }
            
            function countActiveCastle() constant external returns(uint) {
                return activeCastleList.length;
            }
            
            function getActiveCastleId(uint index) constant external returns(uint32) {
                return activeCastleList[index];
            }
            
            function getCastleBasicInfo(address _owner) constant external returns(uint32, uint, uint32) {
                uint32 currentCastleId = trainerCastle[_owner];
                if (currentCastleId == 0)
                    return (0, 0, 0);
                CastleData memory castle = castleData[currentCastleId];
                return (currentCastleId, castle.index, castle.brickNumber);
            }
            
            function getCastleBasicInfoById(uint32 _castleId) constant external returns(uint, address, uint32) {
                CastleData memory castle = castleData[_castleId];
                return (castle.index, castle.owner, castle.brickNumber);
            }
            
            function getCastleObjInfo(uint32 _castleId) constant external returns(uint64, uint64, uint64, uint64, uint64, uint64) {
                CastleData memory castle = castleData[_castleId];
                return (castle.monsters[0], castle.monsters[1], castle.monsters[2], castle.monsters[3], castle.monsters[4], castle.monsters[5]);
            }
            
            function getCastleWinLose(uint32 _castleId) constant external returns(uint32, uint32, uint32) {
                CastleData memory castle = castleData[_castleId];
                return (castle.totalWin, castle.totalLose, castle.brickNumber);
            }
            
            function getCastleStats(uint32 _castleId) constant external returns(string, address, uint32, uint32, uint32, uint) {
                CastleData memory castle = castleData[_castleId];
                return (castle.name, castle.owner, castle.brickNumber, castle.totalWin, castle.totalLose, castle.createTime);
            }
        
            function getBattleDataLog(uint64 _battleId) constant external returns(uint32, address, uint8, uint8, uint8, uint8, uint32, uint32, uint32) {
                BattleDataLog memory battleLog = battles[_battleId];
                return (battleLog.castleId, battleLog.attacker, battleLog.result, battleLog.randoms[0], battleLog.randoms[1], 
                    battleLog.randoms[2], battleLog.castleExps[0], battleLog.castleExps[1], battleLog.castleExps[2]);
            }
            
            function getBattleAttackerLog(uint64 _battleId) constant external returns(uint64, uint64, uint64, uint64, uint64, uint64, uint32, uint32, uint32) {
                BattleDataLog memory battleLog = battles[_battleId];
                return (battleLog.attackerObjIds[0], battleLog.attackerObjIds[1], battleLog.attackerObjIds[2], battleLog.attackerObjIds[3], battleLog.attackerObjIds[4], 
                    battleLog.attackerObjIds[5], battleLog.attackerExps[0], battleLog.attackerExps[1], battleLog.attackerExps[2]);
            }
            
            function getCastleBattleList(uint32 _castleId) constant external returns(uint64, uint64, uint64, uint64) {
                CastleData storage castle = castleData[_castleId];
                return (castle.battleList[0], castle.battleList[1], castle.battleList[2], castle.battleList[3]);
            }
            
            function getTrainerBattleInfo(address _trainer) constant external returns(uint32, uint32, uint32, uint32, uint64, uint64, uint64, uint64) {
                TrainerBattleLog memory trainerLog = trannerBattleLog[_trainer];
                return (trainerLog.totalWin, trainerLog.totalLose, trainerLog.lastCastle, trainerLog.totalBrick, trainerLog.battleList[0], trainerLog.battleList[1], trainerLog.battleList[2], 
                    trainerLog.battleList[3]);
            }
            
            function getTrainerBrick(address _trainer) constant external returns(uint32) {
                return trannerBattleLog[_trainer].totalBrick;
            }
            
            function isOnCastle(uint32 _castleId, uint64 _objId) constant external returns(bool) {
                CastleData storage castle = castleData[_castleId];
                if (castle.index > 0) {
                    for (uint i = 0; i < castle.monsters.length; i++)
                        if (castle.monsters[i] == _objId)
                            return true;
                    return false;
                }
                return false;
            }
        }

        File 5 of 5: EtheremonRankData
        pragma solidity ^0.4.16;
        
        // copyright contact@Etheremon.com
        
        contract BasicAccessControl {
            address public owner;
            // address[] public moderators;
            uint16 public totalModerators = 0;
            mapping (address => bool) public moderators;
            bool public isMaintaining = true;
        
            function BasicAccessControl() public {
                owner = msg.sender;
            }
        
            modifier onlyOwner {
                require(msg.sender == owner);
                _;
            }
        
            modifier onlyModerators() {
                require(msg.sender == owner || moderators[msg.sender] == true);
                _;
            }
        
            modifier isActive {
                require(!isMaintaining);
                _;
            }
        
            function ChangeOwner(address _newOwner) onlyOwner public {
                if (_newOwner != address(0)) {
                    owner = _newOwner;
                }
            }
        
        
            function AddModerator(address _newModerator) onlyOwner public {
                if (moderators[_newModerator] == false) {
                    moderators[_newModerator] = true;
                    totalModerators += 1;
                }
            }
            
            function RemoveModerator(address _oldModerator) onlyOwner public {
                if (moderators[_oldModerator] == true) {
                    moderators[_oldModerator] = false;
                    totalModerators -= 1;
                }
            }
        
            function UpdateMaintaining(bool _isMaintaining) onlyOwner public {
                isMaintaining = _isMaintaining;
            }
        }
        
        
        contract EtheremonRankData is BasicAccessControl {
        
            struct PlayerData {
                address trainer;
                uint32 point;
                uint32 energy;
                uint lastClaim;
                uint32 totalWin;
                uint32 totalLose;
                uint64[6] monsters;
            }
            
            mapping(uint32 => PlayerData) players;
            mapping(address => uint32) playerIds;
            
            uint32 public totalPlayer = 0;
            uint32 public startingPoint = 1200;
            
            // only moderators
            /*
            TO AVOID ANY BUGS, WE ALLOW MODERATORS TO HAVE PERMISSION TO ALL THESE FUNCTIONS AND UPDATE THEM IN EARLY BETA STAGE.
            AFTER THE SYSTEM IS STABLE, WE WILL REMOVE OWNER OF THIS SMART CONTRACT AND ONLY KEEP ONE MODERATOR WHICH IS ETHEREMON BATTLE CONTRACT.
            HENCE, THE DECENTRALIZED ATTRIBUTION IS GUARANTEED.
            */
            
            function updateConfig(uint32 _startingPoint) onlyModerators external {
                startingPoint = _startingPoint;
            }
            
            function setPlayer(address _trainer, uint64 _a0, uint64 _a1, uint64 _a2, uint64 _s0, uint64 _s1, uint64 _s2) onlyModerators external returns(uint32 playerId){
                require(_trainer != address(0));
                playerId = playerIds[_trainer];
                
                bool isNewPlayer = false;
                if (playerId == 0) {
                    totalPlayer += 1;
                    playerId = totalPlayer;
                    playerIds[_trainer] = playerId;
                    isNewPlayer = true;
                }
                
                PlayerData storage player = players[playerId];
                if (isNewPlayer)
                    player.point = startingPoint;
                player.trainer = _trainer;
                player.monsters[0] = _a0;
                player.monsters[1] = _a1;
                player.monsters[2] = _a2;
                player.monsters[3] = _s0;
                player.monsters[4] = _s1;
                player.monsters[5] = _s2;
            }
            
            function updatePlayerPoint(uint32 _playerId, uint32 _totalWin, uint32 _totalLose, uint32 _point) onlyModerators external {
                PlayerData storage player = players[_playerId];
                player.point = _point;
                player.totalWin = _totalWin;
                player.totalLose = _totalLose;
            }
            
            function updateEnergy(uint32 _playerId, uint32 _energy, uint _lastClaim) onlyModerators external {
                PlayerData storage player = players[_playerId];
                player.energy = _energy;
                player.lastClaim = _lastClaim;
            }
            
            // read access 
            function getPlayerData(uint32 _playerId) constant external returns(address trainer, uint32 totalWin, uint32 totalLose, uint32 point, 
                uint64 a0, uint64 a1, uint64 a2, uint64 s0, uint64 s1, uint64 s2, uint32 energy, uint lastClaim) {
                PlayerData memory player = players[_playerId];
                return (player.trainer, player.totalWin, player.totalLose, player.point, player.monsters[0], player.monsters[1], player.monsters[2], 
                    player.monsters[3], player.monsters[4], player.monsters[5], player.energy, player.lastClaim);
            }
            
            function getPlayerDataByAddress(address _trainer) constant external returns(uint32 playerId, uint32 totalWin, uint32 totalLose, uint32 point,
                uint64 a0, uint64 a1, uint64 a2, uint64 s0, uint64 s1, uint64 s2, uint32 energy, uint lastClaim) {
                playerId = playerIds[_trainer];
                PlayerData memory player = players[playerId];
                totalWin = player.totalWin;
                totalLose = player.totalLose;
                point = player.point;
                a0 = player.monsters[0];
                a1 = player.monsters[1];
                a2 = player.monsters[2];
                s0 = player.monsters[3];
                s1 = player.monsters[4];
                s2 = player.monsters[5];
                energy = player.energy;
                lastClaim = player.lastClaim;
            }
            
            function isOnBattle(address _trainer, uint64 _objId) constant external returns(bool) {
                uint32 playerId = playerIds[_trainer];
                if (playerId == 0)
                    return false;
                PlayerData memory player = players[playerId];
                for (uint i = 0; i < player.monsters.length; i++)
                    if (player.monsters[i] == _objId)
                        return true;
                return false;
            }
        
            function getPlayerPoint(uint32 _playerId) constant external returns(address trainer, uint32 totalWin, uint32 totalLose, uint32 point) {
                PlayerData memory player = players[_playerId];
                return (player.trainer, player.totalWin, player.totalLose, player.point);
            }
            
            function getPlayerId(address _trainer) constant external returns(uint32 playerId) {
                return playerIds[_trainer];
            }
        
            function getPlayerEnergy(uint32 _playerId) constant external returns(address trainer, uint32 energy, uint lastClaim) {
                PlayerData memory player = players[_playerId];
                trainer = player.trainer;
                energy = player.energy;
                lastClaim = player.lastClaim;
            }
            
            function getPlayerEnergyByAddress(address _trainer) constant external returns(uint32 playerId, uint32 energy, uint lastClaim) {
                playerId = playerIds[_trainer];
                PlayerData memory player = players[playerId];
                energy = player.energy;
                lastClaim = player.lastClaim;
            }
        }