// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; contract CryptoHero is Ownable, IERC721 { using SafeMath for uint256; uint256 public constant maxGen0Hero = 10; uint256 public gen0Counter = 0; uint256 initHeroPrice = 0.01 ether; bytes4 internal constant _ERC721Checksum = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); bytes4 private constant _InterfaceIdERC721 = 0x80ac58cd; bytes4 private constant _InterfaceIdERC165 = 0x01ffc9a7; string private _name; string private _symbol; struct Hero { uint256 genes; uint64 birthTime; uint32 mumId; uint32 dadId; uint16 generation; } Hero[] heros; // map of hero and owner mapping(uint256 => address) public heroOwner; // hero count of owner mapping(address => uint256) ownsNumberOfTokens; // hero in trading mapping(uint256 => address) public approvalOneHero; mapping(address => mapping (address => bool)) private _operatorApprovals; event HeroBirth(address owner, uint256 _heroId, uint256 mumId, uint256 dadId, uint256 genes); event HeroEvolved(uint256 indexed _heroId, uint256 _oldGenes, uint256 _newGenes); event HeroRetired(uint256 indexed _heroId); constructor(string memory dname, string memory dsymbol) { _name = dname; _symbol = dsymbol; _createHero(0, 0, 0, uint256(0), address(0)); } function getContractOwner() external view returns (address contractowner) { return owner(); } function supportsInterface(bytes4 _interfaceId) external pure returns (bool) { return (_interfaceId == _InterfaceIdERC721 || _interfaceId == _InterfaceIdERC165); } /** * for player buy init heros */ function buyInitHero(uint256 genes) public payable returns (uint256) { require(gen0Counter < maxGen0Hero, "Maximum number of Heros is reached. No new hero allowed!"); require(msg.value >= initHeroPrice, "not enough money!"); gen0Counter = SafeMath.add(gen0Counter, 1); return _createHero(0, 0, 0, genes, msg.sender); } function setInitHeroPrice(uint _price) external onlyOwner { initHeroPrice = _price; } function _createHero( uint256 _mumId, uint256 _dadId, uint256 _generation, uint256 _genes, address _owner ) internal returns (uint256) { Hero memory _hero = Hero({ genes: _genes, birthTime: uint64(block.timestamp), mumId: uint32(_mumId), //easier to input 256 and later convert to 32. dadId: uint32(_dadId), generation: uint16(_generation) }); heros.push(_hero); uint256 newHeroId = SafeMath.sub(heros.length, 1);//want to start with zero. _transfer(address(0), _owner, newHeroId);//transfer from nowhere. Creation event. emit HeroBirth(_owner, newHeroId, _mumId, _dadId, _genes); return newHeroId; } function retireHero(uint256 heroId) internal { delete heros[heroId]; emit HeroRetired(heroId); } function evolveHero(uint256 _heroId, uint256 _newGenes) internal { uint256 _oldGenes = heros[_heroId].genes; heros[_heroId].genes = _newGenes; emit HeroEvolved(_heroId, _oldGenes, _newGenes); } function getHero(uint256 tokenId) public view returns ( uint256 genes, uint256 birthTime, uint256 mumId, uint256 dadId, uint256 generation) //code looks cleaner when the params appear here vs. in the return statement. { require(tokenId < heros.length, "Token ID doesn't exist."); Hero storage hero = heros[tokenId];//saves space over using memory, which would make a copy genes = hero.genes; birthTime = uint256(hero.birthTime); mumId = uint256(hero.mumId); dadId = uint256(hero.dadId); generation = uint256(hero.generation); } function getAllHerosOfOwner(address owner) external view returns(uint256[] memory) { uint256[] memory allHerosOfOwner = new uint[](ownsNumberOfTokens[owner]); uint256 j = 0; for (uint256 i = 0; i < heros.length; i++) { if (heroOwner[i] == owner) { allHerosOfOwner[j] = i; j = SafeMath.add(j, 1); } } return allHerosOfOwner; } function balanceOf(address owner) external view returns (uint256 balance) { return ownsNumberOfTokens[owner]; } function totalSupply() external view returns (uint256 total) { return heros.length; } function name() external view returns (string memory tokenName){ return _name; } function symbol() external view returns (string memory tokenSymbol){ return _symbol; } function ownerOf(uint256 tokenId) external view returns (address owner) { require(tokenId < heros.length, "Token ID doesn't exist."); return heroOwner[tokenId]; } function transfer(address to, uint256 tokenId) external { require(to != address(0), "Use the burn function to burn tokens!"); require(to != address(this), "Wrong address, try again!"); require(heroOwner[tokenId] == msg.sender); _transfer(msg.sender, to, tokenId); } function _transfer(address _from, address _to, uint256 _tokenId) internal { require(_to != address(this)); ownsNumberOfTokens[_to] = SafeMath.add(ownsNumberOfTokens[_to], 1); heroOwner[_tokenId] = _to; if (_from != address(0)) { ownsNumberOfTokens[_from] = SafeMath.sub(ownsNumberOfTokens[_from], 1); delete approvalOneHero[_tokenId];//when owner changes, approval must be removed. } emit Transfer(_from, _to, _tokenId); } function approve(address _approved, uint256 _tokenId) external { require(heroOwner[_tokenId] == msg.sender || _operatorApprovals[heroOwner[_tokenId]][msg.sender] == true, "You are not authorized to access this function."); approvalOneHero[_tokenId] = _approved; emit Approval(msg.sender, _approved, _tokenId); } function setApprovalForAll(address _operator, bool _approved) external { require(_operator != msg.sender); _operatorApprovals[msg.sender][_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } function getApproved(uint256 _tokenId) external view returns (address) { require(_tokenId < heros.length, "Token doesn't exist"); return approvalOneHero[_tokenId]; } function isApprovedForAll(address _owner, address _operator) external view returns (bool) { return _operatorApprovals[_owner][_operator]; } function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal { require(_checkERC721Support(_from, _to, _tokenId, _data)); _transfer(_from, _to, _tokenId); } function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns(bool) { if(!_isContract(_to)) { return true; } bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data); //Call onERC721Received in the _to contract return returnData == _ERC721Checksum; //Check return value } function _isContract(address _to) internal view returns (bool) { uint32 size; assembly{ size := extcodesize(_to) } return size > 0; //check if code size > 0; wallets have 0 size. } function _isOwnerOrApproved(address _from, address _to, uint256 _tokenId) internal view returns (bool) { require(_from == msg.sender || approvalOneHero[_tokenId] == msg.sender || _operatorApprovals[_from][msg.sender], "You are not authorized to use this function"); require(heroOwner[_tokenId] == _from, "Owner incorrect"); require(_to != address(0), "Error: Operation would delete this token permanently"); require(_tokenId < heros.length, "Token doesn't exist"); return true; } function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external { _isOwnerOrApproved(_from, _to, _tokenId); _safeTransfer(_from, _to, _tokenId, data); } function safeTransferFrom(address _from, address _to, uint256 _tokenId) external { _isOwnerOrApproved(_from, _to, _tokenId); _safeTransfer(_from, _to, _tokenId, ""); } function transferFrom(address _from, address _to, uint256 _tokenId) external { _isOwnerOrApproved(_from, _to, _tokenId); _transfer(_from, _to, _tokenId); } function _mixDna( uint256 _dadDna, uint256 _mumDna, uint8 random, uint8 randomSeventeenthDigit, uint8 randomPair, uint8 randomNumberForRandomPair ) internal pure returns (uint256){ uint256[9] memory geneArray; uint256 i; uint256 counter = 7; // start on the right end //DNA example: 11 22 33 44 55 66 77 88 9 if(randomSeventeenthDigit == 0){ geneArray[8] = uint8(_mumDna % 10); //this takes the 17th gene from mum. } else { geneArray[8] = uint8(_dadDna % 10); //this takes the 17th gene from dad. } _mumDna = SafeMath.div(_mumDna, 10); // division by 10 removes the last digit _dadDna = SafeMath.div(_dadDna, 10); // division by 10 removes the last digit for (i = 1; i <= 128; i=i*2) { //1, 2 , 4, 8, 16, 32, 64 ,128 if(random & i == 0){ //00000001 geneArray[counter] = uint8(_mumDna % 100); //00000010 etc. } else { //11001011 & geneArray[counter] = uint8(_dadDna % 100); //00000001 will go through random number bitwise } //if(1) - dad gene _mumDna = SafeMath.div(_mumDna, 100); //if(0) - mum gene _dadDna = SafeMath.div(_dadDna, 100); //division by 100 removes last two digits from genes if(counter > 0) { counter = SafeMath.sub(counter, 1); } } geneArray[randomPair] = randomNumberForRandomPair; //extra randomness for random pair. uint256 newGene = 0; //geneArray example: [11, 22, 33, 44, 55, 66, 77, 88, 9] for (i = 0; i < 8; i++) { //8 is number of pairs in array newGene = SafeMath.mul(newGene, 100); //adds two digits to newGene; no digits the first time newGene = SafeMath.add(newGene, geneArray[i]); //adds a pair of genes } newGene = SafeMath.mul(newGene, 10); //add seventeenth digit newGene = SafeMath.add(newGene, geneArray[8]); return newGene; } }