Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- pragma solidity ^0.4.18;
- /// @title Interface for contracts conforming to ERC-721: Deed Standard
- /// @author William Entriken (https://phor.net), et. al.
- /// @dev Specification at https://github.com/ethereum/eips/XXXFinalUrlXXX
- interface ERC721 {
- // COMPLIANCE WITH ERC-165 (DRAFT) /////////////////////////////////////////
- /// @dev ERC-165 (draft) interface signature for itself
- // bytes4 internal constant INTERFACE_SIGNATURE_ERC165 = // 0x01ffc9a7
- // bytes4(keccak256('supportsInterface(bytes4)'));
- /// @dev ERC-165 (draft) interface signature for ERC721
- // bytes4 internal constant INTERFACE_SIGNATURE_ERC721 = // 0xda671b9b
- // bytes4(keccak256('ownerOf(uint256)')) ^
- // bytes4(keccak256('countOfDeeds()')) ^
- // bytes4(keccak256('countOfDeedsByOwner(address)')) ^
- // bytes4(keccak256('deedOfOwnerByIndex(address,uint256)')) ^
- // bytes4(keccak256('approve(address,uint256)')) ^
- // bytes4(keccak256('takeOwnership(uint256)'));
- /// @notice Query a contract to see if it supports a certain interface
- /// @dev Returns `true` the interface is supported and `false` otherwise,
- /// returns `true` for INTERFACE_SIGNATURE_ERC165 and
- /// INTERFACE_SIGNATURE_ERC721, see ERC-165 for other interface signatures.
- function supportsInterface(bytes4 _interfaceID) external pure returns (bool);
- // PUBLIC QUERY FUNCTIONS //////////////////////////////////////////////////
- /// @notice Find the owner of a deed
- /// @param _deedId The identifier for a deed we are inspecting
- /// @dev Deeds assigned to zero address are considered invalid, and
- /// queries about them do throw.
- /// @return The non-zero address of the owner of deed `_deedId`, or `throw`
- /// if deed `_deedId` is not tracked by this contract
- function ownerOf(uint256 _deedId) external view returns (address _owner);
- /// @notice Count deeds tracked by this contract
- /// @return A count of valid deeds tracked by this contract, where each one of
- /// them has an assigned and queryable owner not equal to the zero address
- function countOfDeeds() external view returns (uint256 _count);
- /// @notice Count all deeds assigned to an owner
- /// @dev Throws if `_owner` is the zero address, representing invalid deeds.
- /// @param _owner An address where we are interested in deeds owned by them
- /// @return The number of deeds owned by `_owner`, possibly zero
- function countOfDeedsByOwner(address _owner) external view returns (uint256 _count);
- /// @notice Enumerate deeds assigned to an owner
- /// @dev Throws if `_index` >= `countOfDeedsByOwner(_owner)` or if
- /// `_owner` is the zero address, representing invalid deeds.
- /// @param _owner An address where we are interested in deeds owned by them
- /// @param _index A counter less than `countOfDeedsByOwner(_owner)`
- /// @return The identifier for the `_index`th deed assigned to `_owner`,
- /// (sort order not specified)
- function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId);
- // TRANSFER MECHANISM //////////////////////////////////////////////////////
- /// @dev This event emits when ownership of any deed changes by any
- /// mechanism. This event emits when deeds are created (`from` == 0) and
- /// destroyed (`to` == 0). Exception: during contract creation, any
- /// transfers may occur without emitting `Transfer`. At the time of any transfer,
- /// the "approved taker" is implicitly reset to the zero address.
- event Transfer(address indexed from, address indexed to, uint256 indexed deedId);
- /// @dev The Approve event emits to log the "approved taker" for a deed -- whether
- /// set for the first time, reaffirmed by setting the same value, or setting to
- /// a new value. The "approved taker" is the zero address if nobody can take the
- /// deed now or it is an address if that address can call `takeOwnership` to attempt
- /// taking the deed. Any change to the "approved taker" for a deed SHALL cause
- /// Approve to emit. However, an exception, the Approve event will not emit when
- /// Transfer emits, this is because Transfer implicitly denotes the "approved taker"
- /// is reset to the zero address.
- event Approval(address indexed owner, address indexed approved, uint256 indexed deedId);
- /// @notice Set the "approved taker" for your deed, or revoke approval by
- /// setting the zero address. You may `approve` any number of times while
- /// the deed is assigned to you, only the most recent approval matters. Emits
- /// an Approval event.
- /// @dev Throws if `msg.sender` does not own deed `_deedId` or if `_to` ==
- /// `msg.sender` or if `_deedId` is not a valid deed.
- /// @param _deedId The deed for which you are granting approval
- function approve(address _to, uint256 _deedId) external payable;
- /// @notice Become owner of a deed for which you are currently approved
- /// @dev Throws if `msg.sender` is not approved to become the owner of
- /// `deedId` or if `msg.sender` currently owns `_deedId` or if `_deedId is not a
- /// valid deed.
- /// @param _deedId The deed that is being transferred
- function takeOwnership(uint256 _deedId) external payable;
- }
- contract Ownable {
- address public owner;
- /**
- * @dev The Ownable constructor sets the original `owner` of the contract to the sender
- * account.
- */
- function Ownable() public {
- owner = msg.sender;
- }
- /**
- * @dev Throws if called by any account other than the owner.
- */
- modifier onlyOwner() {
- require(msg.sender == owner);
- _;
- }
- /**
- * @dev Allows the current owner to transfer control of the contract to a newOwner.
- * @param newOwner The address to transfer ownership to.
- */
- function transferOwnership(address newOwner) public onlyOwner {
- if (newOwner != address(0)) {
- owner = newOwner;
- }
- }
- }
- contract MonsterAccessControl {
- event ContractUpgrade(address newContract);
- // The addresses of the accounts (or contracts) that can execute actions within each roles.
- address public adminAddress;
- /// @dev Access modifier for CEO-only functionality
- modifier onlyAdmin() {
- require(msg.sender == adminAddress);
- _;
- }
- }
- // This contract stores all data on the blockchain
- // only our other contracts can interact with this
- // the data here will be valid for all eternity even if other contracts get updated
- // this way we can make sure that our Monsters have a hard-coded value attached to them
- // that no one including us can change(!)
- contract MonstersData {
- address coreContract;
- struct Monster {
- // timestamp of block when this monster was spawned/created
- uint64 birthTime;
- // generation number
- // gen0 is the very first generation - the later monster spawn the less likely they are to have
- // special attributes and stats
- uint16 generation;
- uint16 mID; // this id (from 1 to 151) is responsible for everything visually like showing the real deal!
- bool tradeable;
- // breeding
- bool female;
- // is this monster exceptionally rare?
- bool shiny;
- }
- // lv1 base stats
- struct MonsterBaseStats {
- uint16 hp;
- uint16 attack;
- uint16 defense;
- uint16 spAttack;
- uint16 spDefense;
- uint16 speed;
- }
- struct Trainer {
- // timestamp of block when this player/trainer was created
- uint64 birthTime;
- // add username
- string username;
- // current area in the "world"
- uint16 currArea;
- address owner;
- }
- // take timestamp of block this game was created on the blockchain
- uint64 creationBlock = uint64(now);
- }
- contract MonstersBase is MonsterAccessControl, MonstersData {
- /// @dev Transfer event as defined in current draft of ERC721. Emitted every time a monster
- /// ownership is assigned, including births.
- event Transfer(address from, address to, uint256 tokenId);
- bool lockedMonsterCreator = false;
- MonsterAuction public monsterAuction;
- MonsterCreatorInterface public monsterCreator;
- function setMonsterCreatorAddress(address _address) external onlyAdmin {
- // only set this once so we (the devs) can't cheat!
- require(!lockedMonsterCreator);
- MonsterCreatorInterface candidateContract = MonsterCreatorInterface(_address);
- monsterCreator = candidateContract;
- lockedMonsterCreator = true;
- }
- // An approximation of currently how many seconds are in between blocks.
- uint256 public secondsPerBlock = 15;
- // array containing all monsters in existence
- Monster[] monsters;
- uint8[] areas;
- uint8 areaIndex = 0;
- mapping(address => Trainer) public addressToTrainer;
- /// @dev A mapping from monster IDs to the address that owns them. All monster have
- /// some valid owner address, even gen0 monster are created with a non-zero owner.
- mapping (uint256 => address) public monsterIndexToOwner;
- // @dev A mapping from owner address to count of tokens that address owns.
- // Used internally inside balanceOf() to resolve ownership count.
- mapping (address => uint256) ownershipTokenCount;
- mapping (uint256 => address) public monsterIndexToApproved;
- mapping (uint256 => string) public monsterIdToNickname;
- mapping (uint256 => bool) public monsterIdToTradeable;
- mapping (uint256 => uint256) public monsterIdToGeneration;
- mapping (uint256 => uint8[7]) public monsterIdToIVs;
- // adds new area to world
- function _createArea() internal {
- areaIndex++;
- areas.push(areaIndex);
- }
- function _createMonster(uint256 _generation, address _owner, uint256 _mID, bool _tradeable,
- bool _female, bool _shiny) internal returns (uint)
- {
- Monster memory _monster = Monster({
- generation: uint16(_generation),
- birthTime: uint64(now),
- mID: uint16(_mID),
- tradeable: _tradeable,
- female: _female,
- shiny: _shiny
- });
- uint256 newMonsterId = monsters.push(_monster) - 1;
- require(newMonsterId == uint256(uint32(newMonsterId)));
- monsterIdToNickname[newMonsterId] = "";
- _transfer(0, _owner, newMonsterId);
- return newMonsterId;
- }
- function _createTrainer(string _username, uint16 _starterId, address _owner) internal returns (uint mon) {
- Trainer memory _trainer = Trainer({
- birthTime: uint64(now),
- username: string(_username),
- // sets to first area!,
- currArea: uint16(1),
- owner: address(_owner)
- });
- addressToTrainer[_owner] = _trainer;
- bool gender = monsterCreator.getMonsterGender();
- // starters cannot be traded and are not shiny
- if (_starterId == 1) {
- mon = _createMonster(0, _owner, 1, false, gender, false);
- } else if (_starterId == 2) {
- mon = _createMonster(0, _owner, 4, false, gender, false);
- } else if (_starterId == 3) {
- mon = _createMonster(0, _owner, 7, false, gender, false);
- }
- }
- function _moveToArea(uint16 _newArea, address player) internal {
- addressToTrainer[player].currArea = _newArea;
- }
- // assigns ownership of monster to address
- function _transfer(address _from, address _to, uint256 _tokenId) internal {
- ownershipTokenCount[_to]++;
- monsterIndexToOwner[_tokenId] = _to;
- if (_from != address(0)) {
- ownershipTokenCount[_from]--;
- // clear any previously approved ownership exchange
- delete monsterIndexToApproved[_tokenId];
- }
- // Emit Transfer event
- Transfer(_from, _to, _tokenId);
- }
- // Only admin can fix how many seconds per blocks are currently observed.
- function setSecondsPerBlock(uint256 secs) external onlyAdmin {
- //require(secs < cooldowns[0]);
- secondsPerBlock = secs;
- }
- }
- contract MonsterOwnership is MonstersBase, ERC721 {
- function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
- return monsterIndexToOwner[_tokenId] == _claimant;
- }
- function _isTradeable(uint256 _tokenId) public view returns (bool) {
- return monsterIdToTradeable[_tokenId];
- }
- /// @dev Checks if a given address currently has transferApproval for a particular monster.
- /// @param _claimant the address we are confirming monster is approved for.
- /// @param _tokenId monster id, only valid when > 0
- function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
- return monsterIndexToApproved[_tokenId] == _claimant;
- }
- function balanceOf(address _owner) public view returns (uint256 count) {
- return ownershipTokenCount[_owner];
- }
- function transfer(address _to, uint256 _tokenId) public payable {
- transferFrom(msg.sender, _to, _tokenId);
- }
- function transferFrom(address _from, address _to, uint256 _tokenId) public payable {
- require(monsterIdToTradeable[_tokenId]);
- // Safety check to prevent against an unexpected 0x0 default.
- require(_to != address(0));
- // Disallow transfers to this contract to prevent accidental misuse.
- // The contract should never own any monsters (except very briefly
- // after a gen0 monster is created and before it goes on auction).
- require(_to != address(this));
- // Check for approval and valid ownership
- require(_owns(_from, _tokenId));
- // checks if _to was aproved
- require(_from == msg.sender || msg.sender == address(monsterAuction) || _approvedFor(_to, _tokenId));
- // Reassign ownership (also clears pending approvals and emits Transfer event).
- _transfer(_from, _to, _tokenId);
- }
- function totalSupply() public view returns (uint) {
- return monsters.length;
- }
- function tokensOfOwner(address _owner) public view returns (uint256[] ownerTokens) {
- uint256 tokenCount = balanceOf(_owner);
- if (tokenCount > 0) {
- uint256[] memory result = new uint256[](tokenCount);
- uint256 totalMonsters = totalSupply();
- uint256 resultIndex = 0;
- uint256 monsterId;
- for (monsterId = 0; monsterId <= totalMonsters; monsterId++) {
- if (monsterIndexToOwner[monsterId] == _owner) {
- result[resultIndex] = monsterId;
- resultIndex++;
- }
- }
- return result;
- }
- return new uint256[](0);
- }
- bytes4 internal constant INTERFACE_SIGNATURE_ERC165 =
- bytes4(keccak256("supportsInterface(bytes4)"));
- bytes4 internal constant INTERFACE_SIGNATURE_ERC721 =
- bytes4(keccak256("ownerOf(uint256)")) ^
- bytes4(keccak256("countOfDeeds()")) ^
- bytes4(keccak256("countOfDeedsByOwner(address)")) ^
- bytes4(keccak256("deedOfOwnerByIndex(address,uint256)")) ^
- bytes4(keccak256("approve(address,uint256)")) ^
- bytes4(keccak256("takeOwnership(uint256)"));
- function supportsInterface(bytes4 _interfaceID) external pure returns (bool) {
- return _interfaceID == INTERFACE_SIGNATURE_ERC165 || _interfaceID == INTERFACE_SIGNATURE_ERC721;
- }
- function ownerOf(uint256 _deedId) external view returns (address _owner) {
- var owner = monsterIndexToOwner[_deedId];
- require(owner != address(0));
- return owner;
- }
- function _approve(uint256 _tokenId, address _approved) internal {
- monsterIndexToApproved[_tokenId] = _approved;
- }
- function countOfDeeds() external view returns (uint256 _count) {
- return totalSupply();
- }
- function countOfDeedsByOwner(address _owner) external view returns (uint256 _count) {
- var arr = tokensOfOwner(_owner);
- return arr.length;
- }
- function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId) {
- return tokensOfOwner(_owner)[_index];
- }
- function approve(address _to, uint256 _tokenId) external payable {
- // Only an owner can grant transfer approval.
- require(_owns(msg.sender, _tokenId));
- // Register the approval (replacing any previous approval).
- monsterIndexToApproved[_tokenId] = _to;
- // Emit approval event.
- Approval(msg.sender, _to, _tokenId);
- }
- function takeOwnership(uint256 _deedId) external payable {
- transferFrom(this.ownerOf(_deedId), msg.sender, _deedId);
- }
- }
- contract MonsterAuctionBase {
- // Reference to contract tracking NFT ownership
- MonsterOwnership public nonFungibleContract;
- ChainMonstersCore public core;
- struct Auction {
- // current owner
- address seller;
- // price in wei
- uint256 price;
- // time when auction started
- uint64 startedAt;
- uint256 id;
- }
- // Cut owner takes on each auction, measured in basis points (1/100 of a percent).
- // Values 0-10,000 map to 0%-100%
- uint256 public ownerCut;
- // Map from token ID to their corresponding auction.
- mapping(uint256 => Auction) tokenIdToAuction;
- mapping(uint256 => address) public auctionIdToSeller;
- mapping (address => uint256) public ownershipAuctionCount;
- event AuctionCreated(uint256 tokenId, uint256 price, uint256 uID, address seller);
- event AuctionSuccessful(uint256 tokenId, uint256 price, address newOwner, uint256 uID);
- event AuctionCancelled(uint256 tokenId, uint256 uID);
- function _transfer(address _receiver, uint256 _tokenId) internal {
- // it will throw if transfer fails
- nonFungibleContract.transfer(_receiver, _tokenId);
- }
- function _addAuction(uint256 _tokenId, Auction _auction) internal {
- tokenIdToAuction[_tokenId] = _auction;
- AuctionCreated(
- uint256(_tokenId),
- uint256(_auction.price),
- uint256(_auction.id),
- address(_auction.seller)
- );
- }
- function _cancelAuction(uint256 _tokenId, address _seller) internal {
- Auction storage _auction = tokenIdToAuction[_tokenId];
- uint256 uID = _auction.id;
- _removeAuction(_tokenId);
- ownershipAuctionCount[_seller]--;
- _transfer(_seller, _tokenId);
- AuctionCancelled(_tokenId, uID);
- }
- function _buy(uint256 _tokenId, uint256 _bidAmount) internal returns (uint256) {
- Auction storage auction = tokenIdToAuction[_tokenId];
- require(_isOnAuction(auction));
- uint256 price = auction.price;
- require(_bidAmount >= price);
- address seller = auction.seller;
- uint256 uID = auction.id;
- // Auction Bid looks fine! so remove
- _removeAuction(_tokenId);
- ownershipAuctionCount[seller]--;
- if (price > 0) {
- uint256 auctioneerCut = _computeCut(price);
- uint256 sellerProceeds = price - auctioneerCut;
- // NOTE: Doing a transfer() in the middle of a complex
- // method like this is generally discouraged because of
- // reentrancy attacks and DoS attacks if the seller is
- // a contract with an invalid fallback function. We explicitly
- // guard against reentrancy attacks by removing the auction
- // before calling transfer(), and the only thing the seller
- // can DoS is the sale of their own asset! (And if it's an
- // accident, they can call cancelAuction(). )
- if (seller != address(core)) {
- seller.transfer(sellerProceeds);
- }
- }
- // Calculate any excess funds included with the bid. If the excess
- // is anything worth worrying about, transfer it back to bidder.
- // NOTE: We checked above that the bid amount is greater than or
- // equal to the price so this cannot underflow.
- uint256 bidExcess = _bidAmount - price;
- // Return the funds. Similar to the previous transfer, this is
- // not susceptible to a re-entry attack because the auction is
- // removed before any transfers occur.
- msg.sender.transfer(bidExcess);
- // Tell the world!
- AuctionSuccessful(_tokenId, price, msg.sender, uID);
- return price;
- }
- function _removeAuction(uint256 _tokenId) internal {
- delete tokenIdToAuction[_tokenId];
- }
- function _isOnAuction(Auction storage _auction) internal view returns (bool) {
- return (_auction.startedAt > 0);
- }
- function _computeCut(uint256 _price) internal view returns (uint256) {
- // NOTE: We don't use SafeMath (or similar) in this function because
- // all of our entry functions carefully cap the maximum values for
- // currency (at 128-bits), and ownerCut <= 10000 (see the require()
- // statement in the ClockAuction constructor). The result of this
- // function is always guaranteed to be <= _price.
- return _price * ownerCut / 10000;
- }
- }
- contract MonsterAuction is MonsterAuctionBase, Ownable {
- bool public isMonsterAuction = true;
- uint256 public auctionIndex = 0;
- function MonsterAuction(address _nftAddress, uint256 _cut) public {
- require(_cut <= 10000);
- ownerCut = _cut;
- var candidateContract = MonsterOwnership(_nftAddress);
- nonFungibleContract = candidateContract;
- ChainMonstersCore candidateCoreContract = ChainMonstersCore(_nftAddress);
- core = candidateCoreContract;
- }
- // only possible to decrease ownerCut!
- function setOwnerCut(uint256 _cut) external onlyOwner {
- require(_cut <= ownerCut);
- ownerCut = _cut;
- }
- function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
- return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
- }
- function _escrow(address _owner, uint256 _tokenId) internal {
- // it will throw if transfer fails
- nonFungibleContract.transferFrom(_owner, this, _tokenId);
- }
- function withdrawBalance() external onlyOwner {
- uint256 balance = this.balance;
- owner.transfer(balance);
- }
- function tokensInAuctionsOfOwner(address _owner) external view returns(uint256[] auctionTokens) {
- uint256 numAuctions = ownershipAuctionCount[_owner];
- uint256[] memory result = new uint256[](numAuctions);
- uint256 totalAuctions = core.totalSupply();
- uint256 resultIndex = 0;
- uint256 auctionId;
- for (auctionId = 0; auctionId <= totalAuctions; auctionId++) {
- Auction storage auction = tokenIdToAuction[auctionId];
- if (auction.seller == _owner) {
- result[resultIndex] = auctionId;
- resultIndex++;
- }
- }
- return result;
- }
- function createAuction(uint256 _tokenId, uint256 _price, address _seller) external {
- require(_seller != address(0));
- require(_price == uint256(_price));
- require(core._isTradeable(_tokenId));
- require(_owns(msg.sender, _tokenId));
- _escrow(msg.sender, _tokenId);
- Auction memory auction = Auction(
- _seller,
- uint256(_price),
- uint64(now),
- uint256(auctionIndex)
- );
- auctionIdToSeller[auctionIndex] = _seller;
- ownershipAuctionCount[_seller]++;
- auctionIndex++;
- _addAuction(_tokenId, auction);
- }
- function buy(uint256 _tokenId) external payable {
- //delete auctionIdToSeller[_tokenId];
- // buy will throw if the bid or funds transfer fails
- _buy (_tokenId, msg.value);
- _transfer(msg.sender, _tokenId);
- }
- function cancelAuction(uint256 _tokenId) external {
- Auction storage auction = tokenIdToAuction[_tokenId];
- require(_isOnAuction(auction));
- address seller = auction.seller;
- require(msg.sender == seller);
- _cancelAuction(_tokenId, seller);
- }
- function getAuction(uint256 _tokenId) external view returns (address seller, uint256 price, uint256 startedAt) {
- Auction storage auction = tokenIdToAuction[_tokenId];
- require(_isOnAuction(auction));
- return (
- auction.seller,
- auction.price,
- auction.startedAt
- );
- }
- function getPrice(uint256 _tokenId) external view returns (uint256) {
- Auction storage auction = tokenIdToAuction[_tokenId];
- require(_isOnAuction(auction));
- return auction.price;
- }
- }
- contract ChainMonstersAuction is MonsterOwnership {
- bool lockedMonsterAuction = false;
- function setMonsterAuctionAddress(address _address) external onlyAdmin {
- require(!lockedMonsterAuction);
- MonsterAuction candidateContract = MonsterAuction(_address);
- require(candidateContract.isMonsterAuction());
- monsterAuction = candidateContract;
- lockedMonsterAuction = true;
- }
- uint256 public constant PROMO_CREATION_LIMIT = 5000;
- uint256 public constant GEN0_CREATION_LIMIT = 5000;
- // Counts the number of monster the contract owner has created.
- uint256 public promoCreatedCount;
- uint256 public gen0CreatedCount;
- // its stats are completely dependent on the spawn alghorithm
- function createPromoMonster(uint256 _mId, address _owner) external onlyAdmin {
- // during generation we have to keep in mind that we have only 10,000 tokens available
- // which have to be divided by 151 monsters, some rarer than others
- // see WhitePaper for gen0/promo monster plan
- // sanity check that this monster ID is actually in game yet
- require(monsterCreator.baseStats(_mId, 1) > 0);
- require(promoCreatedCount < PROMO_CREATION_LIMIT);
- promoCreatedCount++;
- uint8[7] memory ivs = uint8[7](monsterCreator.getGen0IVs());
- bool gender = monsterCreator.getMonsterGender();
- bool shiny = false;
- if (ivs[6] == 1) {
- shiny = true;
- }
- uint256 monsterId = _createMonster(0, _owner, _mId, true, gender, shiny);
- monsterIdToTradeable[monsterId] = true;
- monsterIdToIVs[monsterId] = ivs;
- }
- function createGen0Auction(uint256 _mId, uint256 price) external onlyAdmin {
- // sanity check that this monster ID is actually in game yet
- require(monsterCreator.baseStats(_mId, 1) > 0);
- require(gen0CreatedCount < GEN0_CREATION_LIMIT);
- uint8[7] memory ivs = uint8[7](monsterCreator.getGen0IVs());
- bool gender = monsterCreator.getMonsterGender();
- bool shiny = false;
- if (ivs[6] == 1) {
- shiny = true;
- }
- uint256 monsterId = _createMonster(0, this, _mId, true, gender, shiny);
- monsterIdToTradeable[monsterId] = true;
- _approve(monsterId, monsterAuction);
- monsterIdToIVs[monsterId] = ivs;
- monsterAuction.createAuction(monsterId, price, address(this));
- gen0CreatedCount++;
- }
- }
- // used during launch for world championship
- // can and will be upgraded during development with new battle system!
- // this is just to give players something to do and test their monsters
- // also demonstrates how we can build up more mechanics on top of our locked core contract!
- contract MonsterChampionship is Ownable {
- bool public isMonsterChampionship = true;
- ChainMonstersCore core;
- // list of top ten
- address[10] topTen;
- // holds the address current "world" champion
- address public currChampion;
- mapping (address => uint256) public addressToPowerlevel;
- mapping (uint256 => address) public rankToAddress;
- // try to beat every other player in the top10 with your strongest monster!
- // effectively looping through all top10 players, beating them one by one
- // and if strong enough placing your in the top10 as well
- function contestChampion(uint256 _tokenId) external {
- uint maxIndex = 9;
- // fail tx if player is already champion!
- // in theory players could increase their powerlevel by contesting themselves but
- // this check stops that from happening so other players have the chance to
- // become the temporary champion!
- if (currChampion == msg.sender) {
- revert();
- }
- require(core.isTrainer(msg.sender));
- require(core.monsterIndexToOwner(_tokenId) == msg.sender);
- uint myPowerlevel = 10; // todo add calculation method to this contract!
- // checks if this transaction is useless
- // since we can't fight against ourself!
- // also stops reentrancy attacks
- require(myPowerlevel > addressToPowerlevel[msg.sender]);
- uint myRank = 0;
- for (uint i = 0; i <= maxIndex; i++) {
- if (myPowerlevel > addressToPowerlevel[topTen[i]]) {
- // you have beaten this one so increase temporary rank
- myRank = i;
- if (myRank == maxIndex) {
- currChampion = msg.sender;
- }
- }
- }
- addressToPowerlevel[msg.sender] = myPowerlevel;
- address[10] storage newTopTen = topTen;
- if (currChampion == msg.sender) {
- for (uint j = 0; j < maxIndex; j++) {
- // remove ourselves from this list in case
- if (newTopTen[j] == msg.sender) {
- newTopTen[j] = 0x0;
- break;
- }
- }
- }
- for (uint x = 0; x <= myRank; x++) {
- if (x == myRank) {
- newTopTen[x] = msg.sender;
- } else {
- if (x < maxIndex)
- newTopTen[x] = topTen[x+1];
- }
- }
- topTen = newTopTen;
- }
- function getTopPlayers() external view returns (address[10] players) {
- players = topTen;
- }
- function MonsterChampionship(address coreContract) public {
- core = ChainMonstersCore(coreContract);
- }
- function withdrawBalance() external onlyOwner {
- uint256 balance = this.balance;
- owner.transfer(balance);
- }
- }
- // where the not-so-much "hidden" magic happens
- contract MonsterCreatorInterface is Ownable {
- uint8 public lockedMonsterStatsCount = 0;
- uint nonce = 0;
- function rand(uint16 min, uint16 max) public returns (uint16) {
- nonce++;
- uint16 result = (uint16(keccak256(block.blockhash(block.number-1), nonce))%max);
- if (result < min) {
- result = result+min;
- }
- return result;
- }
- mapping(uint256 => uint8[8]) public baseStats;
- function addBaseStats(uint256 _mId, uint8[8] data) external onlyOwner {
- // lock" the stats down forever
- // since hp is never going to be 0 this is a valid check
- // so we have to be extra careful when adding new baseStats!
- require(data[0] > 0);
- require(baseStats[_mId][0] == 0);
- baseStats[_mId] = data;
- }
- function _addBaseStats(uint256 _mId, uint8[8] data) internal {
- baseStats[_mId] = data;
- lockedMonsterStatsCount++;
- }
- function MonsterCreatorInterface() public {
- // these monsters are already down and "locked" down stats/design wise
- _addBaseStats(1, [45, 49, 49, 65, 65, 45, 12, 4]);
- _addBaseStats(2, [60, 62, 63, 80, 80, 60, 12, 4]);
- _addBaseStats(3, [80, 82, 83, 100, 100, 80, 12, 4]);
- _addBaseStats(4, [39, 52, 43, 60, 50, 65, 10, 6]);
- _addBaseStats(5, [58, 64, 58, 80, 65, 80, 10, 6]);
- _addBaseStats(6, [78, 84, 78, 109, 85, 100, 10, 6]);
- _addBaseStats(7, [44, 48, 65, 50, 64, 43, 11, 14]);
- _addBaseStats(8, [59, 63, 80, 65, 80, 58, 11, 14]);
- _addBaseStats(9, [79, 83, 100, 85, 105, 78, 11, 14]);
- _addBaseStats(10, [40, 35, 30, 20, 20, 50, 7, 4]);
- _addBaseStats(149, [55, 50, 45, 135, 95, 120, 8, 14]);
- _addBaseStats(150, [91, 134, 95, 100, 100, 80, 2, 5]);
- _addBaseStats(151, [100, 100, 100, 100, 100, 100, 5, 19]);
- }
- // this serves as a lookup for new monsters to be generated since all monsters
- // of the same id share the base stats
- // also makes it possible to only store the monsterId on core and change this one
- // during evolution process to save gas and additional transactions
- function getMonsterStats( uint256 _mID) external constant returns(uint8[8] stats) {
- stats[0] = baseStats[_mID][0];
- stats[1] = baseStats[_mID][1];
- stats[2] = baseStats[_mID][2];
- stats[3] = baseStats[_mID][3];
- stats[4] = baseStats[_mID][4];
- stats[5] = baseStats[_mID][5];
- stats[6] = baseStats[_mID][6];
- stats[7] = baseStats[_mID][7];
- }
- function getMonsterGender () external returns(bool female) {
- uint16 femaleChance = rand(0, 100);
- if (femaleChance >= 50) {
- female = true;
- }
- }
- // generates randomized IVs for a new monster
- function getMonsterIVs() external returns(uint8[7] ivs) {
- bool shiny = false;
- uint16 chance = rand(1, 8192);
- if (chance == 42) {
- shiny = true;
- }
- // IVs range between 0 and 31
- // stat range modified for shiny monsters!
- if (shiny) {
- ivs[0] = uint8(rand(10, 31));
- ivs[1] = uint8(rand(10, 31));
- ivs[2] = uint8(rand(10, 31));
- ivs[3] = uint8(rand(10, 31));
- ivs[4] = uint8(rand(10, 31));
- ivs[5] = uint8(rand(10, 31));
- ivs[6] = 1;
- } else {
- ivs[0] = uint8(rand(0, 31));
- ivs[1] = uint8(rand(0, 31));
- ivs[2] = uint8(rand(0, 31));
- ivs[3] = uint8(rand(0, 31));
- ivs[4] = uint8(rand(0, 31));
- ivs[5] = uint8(rand(0, 31));
- ivs[6] = 0;
- }
- }
- // gen0 monsters profit from shiny boost while shiny gen0s have potentially even higher IVs!
- // further increasing the rarity by also doubling the shiny chance!
- function getGen0IVs() external returns (uint8[7] ivs) {
- bool shiny = false;
- uint16 chance = rand(1, 4096);
- if (chance == 42) {
- shiny = true;
- }
- if (shiny) {
- ivs[0] = uint8(rand(15, 31));
- ivs[1] = uint8(rand(15, 31));
- ivs[2] = uint8(rand(15, 31));
- ivs[3] = uint8(rand(15, 31));
- ivs[4] = uint8(rand(15, 31));
- ivs[5] = uint8(rand(15, 31));
- ivs[6] = 1;
- } else {
- ivs[0] = uint8(rand(10, 31));
- ivs[1] = uint8(rand(10, 31));
- ivs[2] = uint8(rand(10, 31));
- ivs[3] = uint8(rand(10, 31));
- ivs[4] = uint8(rand(10, 31));
- ivs[5] = uint8(rand(10, 31));
- ivs[6] = 0;
- }
- }
- function withdrawBalance() external onlyOwner {
- uint256 balance = this.balance;
- owner.transfer(balance);
- }
- }
- contract GameLogicContract {
- bool public isGameLogicContract = true;
- function GameLogicContract() public {
- }
- }
- contract OmegaContract {
- bool public isOmegaContract = true;
- function OmegaContract() public {
- }
- }
- contract ChainMonstersCore is ChainMonstersAuction, Ownable {
- // using a bool to enable us to prepare the game
- bool hasLaunched = false;
- // this address will hold future gamelogic in place
- address gameContract;
- // this contract
- address omegaContract;
- function ChainMonstersCore() public {
- adminAddress = msg.sender;
- _createArea(); // area 1
- _createArea(); // area 2
- }
- // we don't know the exact interfaces yet so use the lockedMonsterStats value to determine if the game is "ready"
- // see WhitePaper for explaination for our upgrade and development roadmap
- function setGameLogicContract(address _candidateContract) external onlyOwner {
- require(monsterCreator.lockedMonsterStatsCount() == 151);
- require(GameLogicContract(_candidateContract).isGameLogicContract());
- gameContract = _candidateContract;
- }
- function setOmegaContract(address _candidateContract) external onlyOwner {
- require(OmegaContract(_candidateContract).isOmegaContract());
- omegaContract = _candidateContract;
- }
- // omega contract takes care of all neccessary checks so assume that this is correct(!)
- function evolveMonster(uint256 _tokenId, uint16 _toMonsterId) external {
- require(msg.sender == omegaContract);
- // retrieve current monster struct
- Monster storage mon = monsters[_tokenId];
- // evolving only changes monster ID since this is responsible for base Stats
- // an evolved monster keeps its gender, generation, IVs and EVs
- mon.mID = _toMonsterId;
- }
- // only callable by gameContract after the full game is launched
- // since all additional monsters after the promo/gen0 ones need to use this coreContract
- // contract as well we have to prepare this core for our future updates where
- // players can freely roam the world and hunt ChainMonsters thus generating more
- function spawnMonster(uint256 _mId, address _owner) external {
- require(msg.sender == gameContract);
- uint8[7] memory ivs = uint8[7](monsterCreator.getMonsterIVs());
- bool gender = monsterCreator.getMonsterGender();
- bool shiny = false;
- if (ivs[6] == 1) {
- shiny = true;
- }
- // important to note that the IV generators do not use Gen0 methods and are Generation 1
- // this means there won't be more than the 10,000 Gen0 monsters sold during the development through the marketplace
- uint256 monsterId = _createMonster(1, _owner, _mId, false, gender, shiny);
- monsterIdToTradeable[monsterId] = true;
- monsterIdToIVs[monsterId] = ivs;
- }
- // used to add playable content to the game
- // monsters will only spawn in certain areas so some are locked on release
- // due to the game being in active development on "launch"
- // each monster has a maximum number of 3 areas where it can appear
- function createArea() public onlyAdmin {
- _createArea();
- }
- function createTrainer(string _username, uint16 _starterId) public {
- require(hasLaunched);
- // only one trainer/account per ethereum address
- require(addressToTrainer[msg.sender].owner == 0);
- // valid input check
- require(_starterId == 1 || _starterId == 2 || _starterId == 3);
- uint256 mon = _createTrainer(_username, _starterId, msg.sender);
- // due to stack limitations we have to assign the IVs here:
- monsterIdToIVs[mon] = monsterCreator.getMonsterIVs();
- }
- function changeUsername(string _name) public {
- require(addressToTrainer[msg.sender].owner == msg.sender);
- addressToTrainer[msg.sender].username = _name;
- }
- function changeMonsterNickname(uint256 _tokenId, string _name) public {
- // users won't be able to rename a monster that is part of an auction
- require(_owns(msg.sender, _tokenId));
- // some string checks...?
- monsterIdToNickname[_tokenId] = _name;
- }
- function moveToArea(uint16 _newArea) public {
- require(addressToTrainer[msg.sender].currArea > 0);
- // never allow anyone to move to area 0 or below since this is used
- // to determine if a trainer profile exists in another method!
- require(_newArea > 0);
- // make sure that this area exists yet!
- require(areas.length >= _newArea);
- // when player is not stuck doing something else he can move freely!
- _moveToArea(_newArea, msg.sender);
- }
- // to be changed to retrieve current stats!
- function getMonster(uint256 _id) external view returns (
- uint256 birthTime, uint256 generation, uint8[8] stats,
- uint256 mID, bool tradeable, uint256 uID)
- {
- Monster storage mon = monsters[_id];
- birthTime = uint256(mon.birthTime);
- generation = mon.generation; // hardcoding due to stack too deep error
- mID = uint256(mon.mID);
- tradeable = bool(mon.tradeable);
- // these values are retrieved from monsterCreator
- stats = uint8[8](monsterCreator.getMonsterStats(uint256(mon.mID)));
- // hack to overcome solidity's stack limitation in monster struct....
- uID = _id;
- }
- function isTrainer(address _check) external view returns (bool isTrainer) {
- Trainer storage trainer = addressToTrainer[_check];
- return (trainer.currArea > 0);
- }
- function withdrawBalance() external onlyOwner {
- uint256 balance = this.balance;
- owner.transfer(balance);
- }
- // after we have setup everything we can unlock the game
- // for public
- function launchGame() external onlyOwner {
- hasLaunched = true;
- }
- }
Add Comment
Please, Sign In to add comment