Contract Diff Checker

Contract Name:
ELFCore

Contract Source Code:

File 1 of 1 : ELFCore

// File: utils/ISpawnContract.sol

pragma solidity >=0.8.0 <0.9.0;

interface ISpawnContract{

    /// @dev This event should be fired whenever the address of CoinB is modified.
    event CoinBChanged(address indexed _from,address indexed _to, uint256 _time);

    /// @dev This event should be fired whenever the address of CoinA is modified.
    event CoinAChanged(address indexed _from,address indexed _to, uint256 _time);

    /// @dev Change CoinA contract.
    ///  Caller should always be superAdmin. _to is the address of new CoinA contract.
    function changeCoinA(address addr) external;

    /// @dev Change CoinB contract.
    ///  Caller should always be superAdmin. _to is the address of new CoinB contract.
    function changeCoinB(address addr) external;

    function setELFCore(address addr) external;

    function spawnEgg(uint256 seed, uint256 momGene, uint256 dadGene, uint256 momChildren, uint256 dadChildren, address caller, bool momFromChaos, bool dadFromChaos) external returns(uint256 gene);
}
// File: utils/IGetter.sol

pragma solidity >=0.8.0 <0.9.0;

interface IGetter {

    /// @dev Interface used by server to check who can use the _tokenId.
    function getUser(address _nftAddress,uint256 _tokenId) external view returns (address);
    
    /// @dev Interface used by server to check who can claim coin B earned by _tokenId.
    function getCoinB(address _nftAddress,uint256 _tokenId) external view returns (address);
}
// File: utils/ICapsuleContract.sol

pragma solidity >=0.8.0 <0.9.0;

interface ICapsuleContract{
    function writePriceInfo(uint256 price) external;
    function getPriceInfo() external view returns(uint256 price,uint256 time);
    function createCapsule(address caller,bool triple) external returns(uint256[] memory, uint256);
    function setELFCoreAddress(address addr) external;
}
// File: utils/Address.sol

pragma solidity >=0.8.0 <0.9.0;

/**
 * @dev Utility library of inline functions on addresses.
 * @notice Based on:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
 * Requires EIP-1052.
 */
library AddressUtils{

    /**
    * @dev Returns whether the target address is a contract.
    * @param _addr Address to check.
    * @return addressCheck True if _addr is a contract, false if not.
    */
    function isContract(
    address _addr
    )
    internal
    view
    returns (bool addressCheck)
    {
    // This method relies in extcodesize, which returns 0 for contracts in
    // construction, since the code is only stored at the end of the
    // constructor execution.

    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
    // for accounts without code, i.e. `keccak256('')`
    bytes32 codehash;
    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    assembly { codehash := extcodehash(_addr) } // solhint-disable-line
    addressCheck = (codehash != 0x0 && codehash != accountHash);
    }

}
// File: utils/IERC165.sol

pragma solidity >=0.8.0 <0.9.0;

interface IERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
// File: token/IERC721Metadata.sol

pragma solidity >=0.8.0 <0.9.0;

/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface IERC721Metadata /* is ERC721 */ {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns (string memory _name);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns (string memory _symbol);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string memory);
}
// File: token/IERC721TokenReceiver.sol

pragma solidity >=0.8.0 <0.9.0;

/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721TokenReceiver {
    /// @notice Handle the receipt of an NFT
    /// @dev The ERC721 smart contract calls this function on the recipient
    ///  after a `transfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _tokenId The NFT identifier which is being transferred
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    ///  unless throwing
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
}
// File: token/IERC721.sol

pragma solidity >=0.8.0 <0.9.0;

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface IERC721 /* is ERC165 */ {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
// File: security/AccessControl.sol

pragma solidity >=0.8.0 <0.9.0;

contract AccessControl{

    /// @dev Error message.
    string constant NO_PERMISSION='no permission';
    string constant INVALID_ADDRESS ='invalid address';
    
    /// @dev Administrator with highest authority. Should be a multisig wallet.
    address payable superAdmin;

    /// @dev Administrator of this contract.
    address payable admin;

    /// Sets the original admin and superAdmin of the contract to the sender account.
    constructor(){
        superAdmin=payable(msg.sender);
        admin=payable(msg.sender);
    }

    /// @dev Throws if called by any account other than the superAdmin.
    modifier onlySuperAdmin{
        require(msg.sender==superAdmin,NO_PERMISSION);
        _;
    }

    /// @dev Throws if called by any account other than the admin.
    modifier onlyAdmin{
        require(msg.sender==admin,NO_PERMISSION);
        _;
    }

    /// @dev Allows the current superAdmin to change superAdmin.
    /// @param addr The address to transfer the right of superAdmin to.
    function changeSuperAdmin(address payable addr) external onlySuperAdmin{
        require(addr!=payable(address(0)),INVALID_ADDRESS);
        superAdmin=addr;
    }

    /// @dev Allows the current superAdmin to change admin.
    /// @param addr The address to transfer the right of admin to.
    function changeAdmin(address payable addr) external onlySuperAdmin{
        require(addr!=payable(address(0)),INVALID_ADDRESS);
        admin=addr;
    }

    /// @dev Called by superAdmin to withdraw balance.
    function withdrawBalance(uint256 amount) external onlySuperAdmin{
        superAdmin.transfer(amount);
    }

    fallback() external {}
}
// File: security/Pausable.sol

pragma solidity >=0.8.0 <0.9.0;


contract Pausable is AccessControl{

    /// @dev Error message.
    string constant PAUSED='paused';
    string constant NOT_PAUSED='not paused';

    /// @dev Keeps track whether the contract is paused. When this is true, most actions are blocked.
    bool public paused = false;

    /// @dev Modifier to allow actions only when the contract is not paused
    modifier whenNotPaused {
        require(!paused,PAUSED);
        _;
    }

    /// @dev Modifier to allow actions only when the contract is paused
    modifier whenPaused {
        require(paused,NOT_PAUSED);
        _;
    }

    /// @dev Called by superAdmin to pause the contract. Used when something goes wrong
    ///  and we need to limit damage.
    function pause() external onlySuperAdmin whenNotPaused {
        paused = true;
    }

    /// @dev Unpauses the smart contract. Can only be called by the superAdmin.
    function unpause() external onlySuperAdmin whenPaused {
        paused = false;
    }
}
// File: ELFCore.sol

pragma solidity >=0.8.0 <0.9.0;










contract ELFBase is Pausable, IERC721TokenReceiver, IERC165, IERC721Metadata{

    /// @dev Used for supportsInterface of ERC165.
    bytes4 constant InterfaceSignature_ERC721=0x80ac58cd;
    bytes4 constant InterfaceSignature_ERC165=0x01ffc9a7;
    bytes4 constant InterfaceSignature_ERC721TokenReceiver=0x150b7a02;
    bytes4 constant InterfaceSignature_ERC721Metadata=0x5b5e139f;
    
    /// @dev Value should be returned when we transfer NFT to a contract via safeTransferFrom.
    bytes4 constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02;

    /// @dev Error message.
    string constant NOT_VALID_NFT ='invalid NFT';

    /// @dev The data type of ELF NFT instance.
    struct ELF{
        uint256 label;
        uint256 dad;
        uint256 mom;
        uint256 gene;
        uint256 bornAt;
    }

    string url='www.roe.com/';

    /// @dev An array contains all existing ELF NFT instances.
    ///  The tokenId of each NFT is actually an index into this array.
    ELF[] ELFs;

    /// @dev Mapping from tokenId to whether it is hatched.
    mapping (uint256 => bool) tokenIdToHatched;

    /// @dev Mapping from tokenId to its children.
    mapping (uint256 => uint256[]) tokenIdToChildren;

    function setURL(string memory _url) external onlyAdmin {
        url=_url;
    }

    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external pure override returns (string memory _name){
        return 'ELF';
    }

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external pure override returns (string memory _symbol){
        return 'ELF';
    }

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view override returns (string memory){
        require(_tokenId<ELFs.length && _tokenId!=0,NOT_VALID_NFT);
        uint256 temp = _tokenId;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (_tokenId != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(_tokenId % 10)));
            _tokenId /= 10;
        }
        return string(abi.encodePacked(url,string(buffer)));
    }

    ///  @dev Returns true for any standardized interfaces implemented by this contract. We implement
    ///  ERC-165, ERC721TokenReceiver and ERC-721.
    function supportsInterface(bytes4 _interfaceID) override external pure returns (bool){
        return ((_interfaceID == InterfaceSignature_ERC165)||(_interfaceID == InterfaceSignature_ERC721)||(_interfaceID==InterfaceSignature_ERC721TokenReceiver)||(_interfaceID == InterfaceSignature_ERC721Metadata));
    }

    /// @dev Required for ERC721TokenReceiver compliance.
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) override pure external returns(bytes4){
        return MAGIC_ON_ERC721_RECEIVED;
    }

    /// @dev Gain information of an ELF instance.
    function gainELF(uint256 _tokenId) external view returns(uint256,uint256,uint256,uint256,uint256,uint256[] memory){
        ELF memory _ELF=ELFs[_tokenId];
        uint256 gene=_ELF.gene;
        if (!tokenIdToHatched[_tokenId]){
            gene=(gene/10000000000000000000000000000000000000000000000000000000000000)%10;
        }
        return(_ELF.label,_ELF.dad,_ELF.mom,gene,_ELF.bornAt,tokenIdToChildren[_tokenId]);
    }
}

contract ELFTransfer is ELFBase, IERC721{

    using AddressUtils for address;

    /// @dev Error message.
    string constant WRONG_PARAMETER='wrong parameter';
    string constant CANT_RECEIVE_NFT='can not receive NFT';

    /// @dev Mapping from tokenId to the address that owns it. There is a valid owner
    ///  for every ELF.
    mapping (uint256 => address) tokenIdToOwner;

    /// @dev Mapping from tokenId to index of arrary storing it.
    mapping (uint256 => uint256) tokenIdToIndex;

    /// @dev Mapping from owner to list of owned tokenIds.
    mapping (address => uint256[]) ownedTokens;

    /// @dev Mapping from tokenId to an address that has been approved to transfer this NFT. 
    ///  A zero value means no approved address.
    mapping (uint256 => address) tokenIdToApproved;

    /// @dev Mapping from owner address to authorized operators of that owner.
    ///  true means authorized, false means unauthorized.
    mapping (address => mapping (address => bool)) ownerToOperators;

    /// @dev Mapping from address to tokenIds usable by the address but not owned by the address.
    mapping (address => uint256[]) ownedByContractTokens;

    /// @dev When the token is owned by smart contract, mapping from tokenId to its usable address.
    ///  Can only be set by the owned smart contract.  
    mapping (uint256 => address) tokenToUsable;

    /// @dev Mapping from tokenId to its index of ownedByContractTokens.
    mapping (uint256 => uint256) tokenToOwnedByContractTokensIndex;

    /// @dev Whether tokenId has been hatched.
    function isHatched(uint256 _tokenId) public view returns(bool res){
        ownerOf(_tokenId);
        res=tokenIdToHatched[_tokenId];
    }

    /// @dev Return owned tokenIds of an address.
    function gainOwnedTokens(address addr) external view returns(uint256[] memory){
        require(addr!=address(0),INVALID_ADDRESS);
        return(ownedTokens[addr]);
    }

    /// @dev Internal function used to add ELF NFT instance to address _to.
    /// @param _to Add token to _to.
    /// @param _tokenId ELF NFT instance we want to operate.
    function _addTo(address _to, uint256 _tokenId) internal{
        tokenIdToOwner[_tokenId]=_to;
        uint256[] storage _ownedTokens=ownedTokens[_to];
        tokenIdToIndex[_tokenId]=_ownedTokens.length;
        _ownedTokens.push(_tokenId);
    }

    /// @dev Internal function used to remove ELF NFT instance.
    /// @param _tokenId ELF NFT instance we want to operate.
    function _removeFrom(address _from,uint256 _tokenId) internal{
        uint256[] storage _ownedTokens=ownedTokens[_from];
        uint256 lastIndex=_ownedTokens.length-1;
        uint256 lastTokenId=_ownedTokens[lastIndex];
        uint256 tokenIndex=tokenIdToIndex[_tokenId];
        delete tokenIdToOwner[_tokenId];
        delete tokenIdToIndex[_tokenId];
        tokenIdToIndex[lastTokenId]=tokenIndex;
        _ownedTokens[tokenIndex]=lastTokenId;
        _ownedTokens.pop();
        if (tokenToUsable[_tokenId]!=address(0)){
            removeFromOwnedByContractTokens(_tokenId);
            tokenToUsable[_tokenId]=address(0);
        }
    }

    function setTokenToUsable(uint256 tokenId, address addr) external{
        require(msg.sender==ownerOf(tokenId) && msg.sender.isContract(),NO_PERMISSION);
        if (tokenToUsable[tokenId]!=address(0)){
            removeFromOwnedByContractTokens(tokenId);
        }
        tokenToUsable[tokenId]=addr;
        tokenToOwnedByContractTokensIndex[tokenId]=ownedByContractTokens[addr].length;
        ownedByContractTokens[addr].push(tokenId);
    }

    function removeFromOwnedByContractTokens(uint256 tokenId) internal{
        address from=tokenToUsable[tokenId];
        uint256 index=tokenToOwnedByContractTokensIndex[tokenId];
        uint256 l=ownedByContractTokens[from].length;
        uint256 lastTokenId=ownedByContractTokens[from][l-1];
        ownedByContractTokens[from][index]=lastTokenId;
        ownedByContractTokens[from].pop();
        tokenToOwnedByContractTokensIndex[lastTokenId]=index;
        delete tokenToOwnedByContractTokensIndex[tokenId];
    }

    /// @dev Return usable tokens of addr.
    function usableTokens(address addr) external view returns(uint256[] memory){
        require(addr!=address(0),INVALID_ADDRESS);
        uint256[] memory temp1=ownedTokens[addr];
        uint256[] memory temp2=ownedByContractTokens[addr];
        uint256 l1=temp1.length;
        uint256 l2=temp2.length;
        uint256[] memory cache1 = new uint256[](l1);
        uint256[] memory cache2 = new uint256[](l2);
        uint256 count1;
        uint256 count2;
        for (uint256 i=0;i<l1;i++){
            if (tokenIdToHatched[temp1[i]]){
                count1++;
                cache1[i]=temp1[i];
            }
        }
        for (uint256 i=0;i<l2;i++){
            if (tokenIdToHatched[temp2[i]]){
                IGetter contractInstance=IGetter(ownerOf(temp2[i]));
                try contractInstance.getUser(address(this),temp2[i]) returns (address tar){
                    if (tar==addr){
                        count2++;
                        cache2[i]=temp2[i];
                    }
                } catch {}
            }
        }
        uint256 index=0;
        uint256[] memory res = new uint256[](count1+count2);
        for (uint256 i=0;i<l1;i++){
            if (cache1[i]!=0){
                res[index]=cache1[i];
                index++;
            }
        }
        for (uint256 i=0;i<l2;i++){
            if (cache2[i]!=0){
                res[index]=cache2[i];
                index++;
            }
        }
        return res;
    }

    /// @dev Required for ERC-721 compliance.
    function balanceOf(address _owner) override external view returns (uint256){
        require(_owner!=address(0),INVALID_ADDRESS);
        return ownedTokens[_owner].length;
    }

    /// @dev Required for ERC-721 compliance.
    function ownerOf(uint256 _tokenId) override public view returns (address res){
        res=tokenIdToOwner[_tokenId];
        require(res!=address(0),NOT_VALID_NFT);
    }

    /// @dev Required for ERC-721 compliance.
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) override external payable{
        transferFrom(_from, _to, _tokenId);
        if (_to.isContract()){
            bytes4 retval=IERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, data);
            require(retval == MAGIC_ON_ERC721_RECEIVED,CANT_RECEIVE_NFT);
        }
    }

    /// @dev Required for ERC-721 compliance.
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) override external payable{
        transferFrom(_from, _to, _tokenId);
        if (_to.isContract()){
            bytes4 retval=IERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, '');
            require(retval == MAGIC_ON_ERC721_RECEIVED,CANT_RECEIVE_NFT);
        }
    }

    /// @dev Required for ERC-721 compliance.
    function transferFrom(address _from, address _to, uint256 _tokenId) override public payable whenNotPaused{
        address _owner=ownerOf(_tokenId);
        require(msg.sender==_owner||ownerToOperators[_owner][msg.sender]||msg.sender==tokenIdToApproved[_tokenId],NO_PERMISSION);
        require(_from==_owner,WRONG_PARAMETER);
        require(_to!=address(0),INVALID_ADDRESS);
        _removeFrom(_from,_tokenId);
        _addTo(_to,_tokenId);
        if (tokenIdToApproved[_tokenId]!=address(0)){
            delete tokenIdToApproved[_tokenId];
            emit Approval(_from, address(0), _tokenId);
        }
        emit Transfer(_from, _to, _tokenId);
    }

    /// @dev Required for ERC-721 compliance.
    function approve(address _approved, uint256 _tokenId) override external payable whenNotPaused{
        address _owner=ownerOf(_tokenId);
        require(msg.sender==_owner||ownerToOperators[_owner][msg.sender],NO_PERMISSION);
        tokenIdToApproved[_tokenId]=_approved;
        emit Approval(_owner,_approved,_tokenId);
    }

    /// @dev Required for ERC-721 compliance.
    function setApprovalForAll(address _operator, bool _approved) override external whenNotPaused{
        ownerToOperators[msg.sender][_operator]=_approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /// @dev Required for ERC-721 compliance.
    function getApproved(uint256 _tokenId) override external view returns (address){
        ownerOf(_tokenId);
        return tokenIdToApproved[_tokenId];
    }

    /// @dev Required for ERC-721 compliance.
    function isApprovedForAll(address _owner, address _operator) override external view returns (bool){
        return ownerToOperators[_owner][_operator];
    }

    ///@dev Only res1 can use tokenId. Coin B earned by tokenId should distribute to res2.
    function userOfCoinBTo(uint256[] memory tokenIds) external view returns (address[] memory,address[] memory){
        uint256 l=tokenIds.length;
        address[] memory res1=new address[](l);
        address[] memory res2=new address[](l);
        address owner;
        uint256 tokenId;
        for (uint256 i=0;i<l;i++){
            tokenId=tokenIds[i];
            owner=ownerOf(tokenId);
            if (tokenIdToHatched[tokenId]){
                if (owner.isContract()){
                    IGetter contractInstance=IGetter(owner);
                    try contractInstance.getUser(address(this),tokenId) returns(address temp){
                        res1[i]=temp;
                    }catch{res1[i]=address(0);}
                    try contractInstance.getCoinB(address(this),tokenId) returns(address temp){
                        res2[i]=temp;
                    }catch{res2[i]=address(0);}
                }
                else{
                    res1[i]=owner;
                    res2[i]=owner;
                }
            }
            else{
                res1[i]=address(0);
                res2[i]=address(0);
            }
        }
        return (res1,res2);
    }

    /// @dev Gain all genes of ELFs in tokenIds.
    function gainGenes(uint256[] memory tokenIds) external view returns(uint256[] memory){
        uint256 l=tokenIds.length;
        uint256[] memory res = new uint256[](l);
        for (uint256 i=0;i<l;i++){
            if (isHatched(tokenIds[i])){
                ELF memory _ELF=ELFs[tokenIds[i]];
                res[i]=_ELF.gene;
            }
            else{
                res[i]=0;
            }
        }
        return res;
    }
}

contract ELFCore is ELFTransfer{

    /// @dev This is the spawan of chaos. Whether it is an ELF is still a mystery. 
    ///  No one has the ability to own it.
    constructor(){
        ELF memory _ELF=ELF({
            label:777,
            dad:0,
            mom:0,
            gene:0,
            bornAt:block.timestamp
        });
        ELFs.push(_ELF);
        tokenIdToHatched[0]=true;
    }

    /// @dev Error message.
    string constant WRONG_MONEY='money not enough';
    string constant EXCEED_MAX_SPAWN_TIMES ='exceed maximum spawan time';
    string constant CLOSE_BREEDING='close breeding';
    string constant NOT_MATURE='egg not mature';
    string constant HATCHED='egg hatched';

    /// @dev This event should be fired whenever the address of capsule contract is modified.
    event CapsuleContractChanged(address indexed _from,address indexed _to, uint256 _time);

    /// @dev The address of current capsule contract.
    address public capsuleContractAddress;

    /// @dev Total capsule number. Can't bigger than 5000.
    uint256 public capsuleCount;

    /// @dev Change capsule contract.
    ///  Caller should always be administrator. addr is the address of new capsule contract.
    function changeCapsuleContract(address addr) external onlySuperAdmin{
        require(addr!=address(0),INVALID_ADDRESS);
        emit CapsuleContractChanged(capsuleContractAddress,addr,block.timestamp);
        capsuleContractAddress=addr;
    }

    /// @dev Capsule machine. Capsule contract is assigned by superAdmin, 
    ///  so we do not have to worry about reentrancy attack here.
    function capsuleMachine(bool triple) external payable whenNotPaused {
        uint256 count=1;
        if (triple){
            count=3;
        }
        capsuleCount+=count;
        require(capsuleCount<=5000,'capsule limit exceeded');
        ICapsuleContract capsuleContractInstance=ICapsuleContract(capsuleContractAddress);
        (uint256 price,)=capsuleContractInstance.getPriceInfo();
        require(msg.value>=price*count,WRONG_MONEY);
        uint256 label;
        uint256[] memory genes = new uint256[](count);
        (genes,label)=capsuleContractInstance.createCapsule(msg.sender,triple);
        for(uint256 i=0;i<count;i++){
            ELF memory _ELF=ELF({
                label:label,
                dad:0,
                mom:0,
                gene:genes[i],
                bornAt:block.timestamp+432000
            });
            _addTo(msg.sender,ELFs.length);
            emit Transfer(address(0),msg.sender,ELFs.length);
            ELFs.push(_ELF);
        }
        if (msg.value>price*count){
            payable(msg.sender).transfer(msg.value-price*count);
        }
    }

    /// @dev This event should be fired whenever the address of SpawnContract is modified.
    event SpawnContractChanged(address indexed _from,address indexed _to, uint256 _time);

    /// @dev The address of SpawnContract.
    address public SpawnContractAddress;

    /// @dev Change SpawnContract address. 
    ///  Caller should always be superAdmin. addr is the address of new SpawnContract address.
    function changeSpawnContract(address addr) external onlySuperAdmin{
        require(addr!=address(0),INVALID_ADDRESS);
        emit SpawnContractChanged(SpawnContractAddress,addr,block.timestamp);
        SpawnContractAddress=addr;
    }

    /// @dev Maximun spawning time of an ELF.
    uint256 public constant maxSpawnTimes=7;
    
    /// @dev Spawn an egg of ELF. Mint NFT. Spawn contract is assigned by superAdmin, 
    ///  so we do not have to worry about reentrancy attack here.
    /// @param momTokenId tokenId of one of parent of new ELF. ELF has no gender, mom and dad is used for convinience.
    /// @param dadTokenId tokenId of one of parent of new ELF.
    function spawnEgg(uint256 momTokenId,uint256 dadTokenId) external whenNotPaused returns (uint256 tokenId){
        require(msg.sender==ownerOf(momTokenId),NO_PERMISSION);
        require(msg.sender==ownerOf(dadTokenId),NO_PERMISSION);
        uint256 dadChildrenCount=tokenIdToChildren[dadTokenId].length;
        uint256 momChildrenCount=tokenIdToChildren[momTokenId].length;
        require(momChildrenCount<maxSpawnTimes,EXCEED_MAX_SPAWN_TIMES);
        require(dadChildrenCount<maxSpawnTimes,EXCEED_MAX_SPAWN_TIMES);
        require(tokenIdToHatched[momTokenId]&&tokenIdToHatched[dadTokenId],WRONG_PARAMETER);
        require(momTokenId!=dadTokenId,WRONG_PARAMETER);
        ELF memory _mom=ELFs[momTokenId];
        ELF memory _dad=ELFs[dadTokenId];
        if (!(fromChaos(_mom) && fromChaos(_dad))){
            require(_mom.mom!=dadTokenId,CLOSE_BREEDING);
            require(_mom.dad!=dadTokenId,CLOSE_BREEDING);
            require(_dad.mom!=momTokenId,CLOSE_BREEDING);
            require(_dad.dad!=momTokenId,CLOSE_BREEDING);
        }
        if (!fromChaos(_mom) && !fromChaos(_dad)){
            require(_mom.mom!=_dad.mom,CLOSE_BREEDING);
            require(_mom.mom!=_dad.dad,CLOSE_BREEDING);
            require(_mom.dad!=_dad.mom,CLOSE_BREEDING);
            require(_mom.dad!=_dad.dad,CLOSE_BREEDING);
        } 
        uint256 seed=block.timestamp+dadChildrenCount+momChildrenCount;
        uint256 gene=ISpawnContract(SpawnContractAddress).spawnEgg(seed,_mom.gene,_dad.gene,momChildrenCount,dadChildrenCount,msg.sender,fromChaos(_mom),fromChaos(_dad));
        ELF memory _ELF=ELF({
            label:0,
            dad:dadTokenId,
            mom:momTokenId,
            gene:gene,
            bornAt:seed+432000
        });
        tokenId=ELFs.length;
        ELFs.push(_ELF);
        _addTo(msg.sender,tokenId);
        tokenIdToChildren[momTokenId].push(tokenId);
        tokenIdToChildren[dadTokenId].push(tokenId);
        emit Transfer(address(0),msg.sender,tokenId);
    }

    /// @dev Hatch an egg.
    function hatchELF(uint256 tokenId) external whenNotPaused{
        ELF memory _ELF=ELFs[tokenId];
        require(!tokenIdToHatched[tokenId],HATCHED);
        require(msg.sender==ownerOf(tokenId),NO_PERMISSION);
        require(block.timestamp>=_ELF.bornAt,NOT_MATURE);
        tokenIdToHatched[tokenId]=true;
    }

    /// @dev Whether the given ELF is from chaos.
    function fromChaos(ELF memory _ELF) internal pure returns(bool){
        return _ELF.mom==0;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):