ETH Price: $2,161.74 (+1.00%)

Transaction Decoder

Block:
7238230 at Feb-19-2019 12:56:36 AM +UTC
Transaction Fee:
0.001530745 ETH $3.31
Gas Used:
306,149 Gas / 5 Gwei

Emitted Events:

53 MLBNFT.Approval( _owner=[Sender] 0x762fba3875baf5dfa20bfc05588ed377dc739f9a, _approved=SaleManager, _tokenId=46937 )
54 MLBNFT.Transfer( _from=[Sender] 0x762fba3875baf5dfa20bfc05588ed377dc739f9a, _to=SaleManager, _tokenId=46937 )
55 SaleManager.SaleCreated( tokenID=46937, startingPrice=50000000000000000, endingPrice=50000000000000000, duration=8640000, startedAt=1550537796 )

Account State Difference:

  Address   Before After State Difference Code
0x762FBA38...7DC739f9A
4.144761589428672738 Eth
Nonce: 2886
4.143230844428672738 Eth
Nonce: 2887
0.001530745
0x8c9b261F...F8c788099
(MiningPoolHub: Old Address)
4,797.724055085357035822 Eth4,797.725585830357035822 Eth0.001530745
0xE7232a9F...90e22990e

Execution Trace

MLBNFT.initiateCreateSale( _tokenId=46937, _startingPrice=50000000000000000, _endingPrice=50000000000000000, _duration=8640000 )
  • SaleManager.createSale( _tokenId=46937, _startingPrice=50000000000000000, _endingPrice=50000000000000000, _duration=8640000, _owner=0x762FBA3875Baf5dfA20BFC05588ed377DC739f9A )
    • MLBNFT.checkIsAttached( _tokenId=46937 ) => ( 0 )
    • MLBNFT.safeTransferFrom( _from=0x762FBA3875Baf5dfA20BFC05588ed377DC739f9A, _to=0xE7232a9Fd8bF427Aa41918BC008D32290e22990e, _tokenId=46937 )
      • SaleManager.onERC721Received( 0xE7232a9Fd8bF427Aa41918BC008D32290e22990e, 0x762FBA3875Baf5dfA20BFC05588ed377DC739f9A, 46937, 0x )
        initiateCreateSale[MLBNFT (ln:2228)]
        File 1 of 2: MLBNFT
        /**
         * Created on 2018-06-05 16:37
         * @summary: Our NFT Minting Contract which inherits ERC721 capability from LSNFT
         * @author: Fazri Zubair & Farhan Khwaja
         */
        pragma solidity ^0.4.23;
        
        pragma solidity ^0.4.23;
        
        /* NFT Metadata Schema 
        {
            "title": "Asset Metadata",
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "Identifies the asset to which this NFT represents",
                },
                "description": {
                    "type": "string",
                    "description": "Describes the asset to which this NFT represents",
                },
                "image": {
                    "type": "string",
                    "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",
                }
            }
        }
        */
        
        /**
         * @title SafeMath
         * @dev Math operations with safety checks that revert on error
         */
        library SafeMath {
        
          /**
          * @dev Multiplies two numbers, reverts on overflow.
          */
          function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
            if (_a == 0) {
              return 0;
            }
        
            uint256 c = _a * _b;
            require(c / _a == _b);
        
            return c;
          }
        
          /**
          * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
          */
          function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
            require(_b > 0); // Solidity only automatically asserts when dividing by 0
            uint256 c = _a / _b;
            // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
        
            return c;
          }
        
          /**
          * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
          */
          function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
            require(_b <= _a);
            uint256 c = _a - _b;
        
            return c;
          }
        
          /**
          * @dev Adds two numbers, reverts on overflow.
          */
          function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
            uint256 c = _a + _b;
            require(c >= _a);
        
            return c;
          }
        }
        
        /**
         * Utility library of inline functions on addresses
         */
        library AddressUtils {
        
            /**
            * Returns whether the target address is a contract
            * @dev This function will return false if invoked during the constructor of a contract,
            *  as the code is not actually created until after the constructor finishes.
            * @param addr address to check
            * @return whether the target address is a contract
            */
            function isContract(address addr) internal view returns (bool) {
                uint256 size;
                // XXX Currently there is no better way to check if there is a contract in an address
                // than to check the size of the code at that address.
                // See https://ethereum.stackexchange.com/a/14016/36603
                // for more details about how this works.
                // TODO Check this again before the Serenity release, because all addresses will be
                // contracts then.
                // solium-disable-next-line security/no-inline-assembly
                assembly { size := extcodesize(addr) }
                return size > 0;
            }
        
        }
        
        /**
         * @title ERC721 Non-Fungible Token Standard basic interface
         * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721Basic {
            event Transfer(
                address indexed _from,
                address indexed _to,
                uint256 indexed _tokenId
            );
            event Approval(
                address indexed _owner,
                address indexed _approved,
                uint256 indexed _tokenId
            );
            event ApprovalForAll(
                address indexed _owner,
                address indexed _operator,
                bool _approved
            );
        
            function balanceOf(address _owner) public view returns (uint256 _balance);
            function ownerOf(uint256 _tokenId) public view returns (address _owner);
            function exists(uint256 _tokenId) public view returns (bool _exists);
        
            function approve(address _to, uint256 _tokenId) public;
            function getApproved(uint256 _tokenId)
                public view returns (address _operator);
        
            function setApprovalForAll(address _operator, bool _approved) public;
            function isApprovedForAll(address _owner, address _operator)
                public view returns (bool);
        
            function transferFrom(address _from, address _to, uint256 _tokenId) public;
            function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;
        
            function safeTransferFrom(
                address _from,
                address _to,
                uint256 _tokenId,
                bytes _data
            )
                public;
        }
        
        /**
         * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
         * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721Enumerable is ERC721Basic {
            function totalSupply() public view returns (uint256);
            function tokenOfOwnerByIndex(
                address _owner,
                uint256 _index
            )
                public
                view
                returns (uint256 _tokenId);
        
            function tokenByIndex(uint256 _index) public view returns (uint256);
        }
        
        /**
         * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
         * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721Metadata is ERC721Basic {
            function name() public view returns (string _name);
            function symbol() public view returns (string _symbol);
            function tokenURI(uint256 _tokenId) public view returns (string);
        }
        
        /**
         * @title ERC-721 Non-Fungible Token Standard, full implementation interface
         * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata {
        }
        
        /**
         * @title ERC721 Non-Fungible Token Standard basic implementation
         * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721BasicToken is ERC721Basic {
            using SafeMath for uint256;
            using AddressUtils for address;
        
            // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
            // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
            bytes4 public constant ERC721_RECEIVED = 0x150b7a02;
        
            // Mapping from token ID to owner
            mapping (uint256 => address) internal tokenOwner;
        
            // Mapping from token ID to approved address
            mapping (uint256 => address) internal tokenApprovals;
        
            // Mapping from owner to number of owned token
            mapping (address => uint256) internal ownedTokensCount;
        
            // Mapping from owner to operator approvals
            mapping (address => mapping (address => bool)) internal operatorApprovals;
        
            /**
            * @dev Guarantees msg.sender is owner of the given token
            * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
            */
            modifier onlyOwnerOf(uint256 _tokenId) {
                require (ownerOf(_tokenId) == msg.sender);
                _;
            }
        
            /**
            * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
            * @param _tokenId uint256 ID of the token to validate
            */
            modifier canTransfer(uint256 _tokenId) {
                require (isApprovedOrOwner(msg.sender, _tokenId));
                _;
            }
        
            /**
            * @dev Gets the balance of the specified address
            * @param _owner address to query the balance of
            * @return uint256 representing the amount owned by the passed address
            */
            function balanceOf(address _owner) public view returns (uint256) {
                require (_owner != address(0));
                return ownedTokensCount[_owner];
            }
        
            /**
            * @dev Gets the owner of the specified token ID
            * @param _tokenId uint256 ID of the token to query the owner of
            * @return owner address currently marked as the owner of the given token ID
            */
            function ownerOf(uint256 _tokenId) public view returns (address) {
                address owner = tokenOwner[_tokenId];
                require (owner != address(0));
                return owner;
            }
        
            /**
            * @dev Returns whether the specified token exists
            * @param _tokenId uint256 ID of the token to query the existence of
            * @return whether the token exists
            */
            function exists(uint256 _tokenId) public view returns (bool) {
                address owner = tokenOwner[_tokenId];
                return owner != address(0);
            }
        
            /**
            * @dev Approves another address to transfer the given token ID
            * @dev The zero address indicates there is no approved address.
            * @dev There can only be one approved address per token at a given time.
            * @dev Can only be called by the token owner or an approved operator.
            * @param _to address to be approved for the given token ID
            * @param _tokenId uint256 ID of the token to be approved
            */
            function approve(address _to, uint256 _tokenId) public {
                address owner = ownerOf(_tokenId);
                require (_to != owner);
                require (msg.sender == owner || isApprovedForAll(owner, msg.sender));
        
                tokenApprovals[_tokenId] = _to;
                emit Approval(owner, _to, _tokenId);
            }
        
            /**
            * @dev Gets the approved address for a token ID, or zero if no address set
            * @param _tokenId uint256 ID of the token to query the approval of
            * @return address currently approved for the given token ID
            */
            function getApproved(uint256 _tokenId) public view returns (address) {
                return tokenApprovals[_tokenId];
            }
        
            /**
            * @dev Sets or unsets the approval of a given operator
            * @dev An operator is allowed to transfer all tokens of the sender on their behalf
            * @param _to operator address to set the approval
            * @param _approved representing the status of the approval to be set
            */
            function setApprovalForAll(address _to, bool _approved) public {
                require (_to != msg.sender);
                operatorApprovals[msg.sender][_to] = _approved;
                emit ApprovalForAll(msg.sender, _to, _approved);
            }
        
            /**
            * @dev Tells whether an operator is approved by a given owner
            * @param _owner owner address which you want to query the approval of
            * @param _operator operator address which you want to query the approval of
            * @return bool whether the given operator is approved by the given owner
            */
            function isApprovedForAll(
                address _owner,
                address _operator
            )
                public
                view
                returns (bool)
            {
                return operatorApprovals[_owner][_operator];
            }
        
            /**
            * @dev Transfers the ownership of a given token ID to another address
            * @dev Usage of this method is discouraged, use `safeTransferFrom` whenever possible
            * @dev Requires the msg sender to be the owner, approved, or operator
            * @param _from current owner of the token
            * @param _to address to receive the ownership of the given token ID
            * @param _tokenId uint256 ID of the token to be transferred
            */
            function transferFrom(
                address _from,
                address _to,
                uint256 _tokenId
            )
                public
                canTransfer(_tokenId)
            {
                require (_from != address(0));
                require (_to != address(0));
        
                clearApproval(_from, _tokenId);
                removeTokenFrom(_from, _tokenId);
                addTokenTo(_to, _tokenId);
        
                emit Transfer(_from, _to, _tokenId);
            }
        
            /**
            * @dev Safely transfers the ownership of a given token ID to another address
            * @dev If the target address is a contract, it must implement `onERC721Received`,
            *  which is called upon a safe transfer, and return the magic value
            *  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
            *  the transfer is reverted.
            * @dev Requires the msg sender to be the owner, approved, or operator
            * @param _from current owner of the token
            * @param _to address to receive the ownership of the given token ID
            * @param _tokenId uint256 ID of the token to be transferred
            */
            function safeTransferFrom(
                address _from,
                address _to,
                uint256 _tokenId
            )
                public
                canTransfer(_tokenId)
            {
                // solium-disable-next-line arg-overflow
                safeTransferFrom(_from, _to, _tokenId, "");
            }
        
            /**
            * @dev Safely transfers the ownership of a given token ID to another address
            * @dev If the target address is a contract, it must implement `onERC721Received`,
            *  which is called upon a safe transfer, and return the magic value
            *  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
            *  the transfer is reverted.
            * @dev Requires the msg sender to be the owner, approved, or operator
            * @param _from current owner of the token
            * @param _to address to receive the ownership of the given token ID
            * @param _tokenId uint256 ID of the token to be transferred
            * @param _data bytes data to send along with a safe transfer check
            */
            function safeTransferFrom(
                address _from,
                address _to,
                uint256 _tokenId,
                bytes _data
            )
                public
                canTransfer(_tokenId)
            {
                transferFrom(_from, _to, _tokenId);
                // solium-disable-next-line arg-overflow
                require (checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
            }
        
            /**
            * @dev Returns whether the given spender can transfer a given token ID
            * @param _spender address of the spender to query
            * @param _tokenId uint256 ID of the token to be transferred
            * @return bool whether the msg.sender is approved for the given token ID,
            *  is an operator of the owner, or is the owner of the token
            */
            function isApprovedOrOwner(
                address _spender,
                uint256 _tokenId
            )
                internal
                view
                returns (bool)
            {
                address owner = ownerOf(_tokenId);
                // Disable solium check because of
                // https://github.com/duaraghav8/Solium/issues/175
                // solium-disable-next-line operator-whitespace
                return (
                _spender == owner ||
                getApproved(_tokenId) == _spender ||
                isApprovedForAll(owner, _spender)
                );
            }
        
            /**
            * @dev Internal function to mint a new token
            * @dev Reverts if the given token ID already exists
            * @param _to The address that will own the minted token
            * @param _tokenId uint256 ID of the token to be minted by the msg.sender
            */
            function _mint(address _to, uint256 _tokenId) internal {
                require (_to != address(0));
                addTokenTo(_to, _tokenId);
                emit Transfer(address(0), _to, _tokenId);
            }
        
            /**
            * @dev Internal function to burn a specific token
            * @dev Reverts if the token does not exist
            * @param _tokenId uint256 ID of the token being burned by the msg.sender
            */
            function _burn(address _owner, uint256 _tokenId) internal {
                clearApproval(_owner, _tokenId);
                removeTokenFrom(_owner, _tokenId);
                emit Transfer(_owner, address(0), _tokenId);
            }
        
            /**
            * @dev Internal function to clear current approval of a given token ID
            * @dev Reverts if the given address is not indeed the owner of the token
            * @param _owner owner of the token
            * @param _tokenId uint256 ID of the token to be transferred
            */
            function clearApproval(address _owner, uint256 _tokenId) internal {
                require (ownerOf(_tokenId) == _owner);
                if (tokenApprovals[_tokenId] != address(0)) {
                    tokenApprovals[_tokenId] = address(0);
                }
            }
        
            /**
            * @dev Internal function to add a token ID to the list of a given address
            * @param _to address representing the new owner of the given token ID
            * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
            */
            function addTokenTo(address _to, uint256 _tokenId) internal {
                require (tokenOwner[_tokenId] == address(0));
                tokenOwner[_tokenId] = _to;
                ownedTokensCount[_to] = ownedTokensCount[_to].add(1);
            }
        
            /**
            * @dev Internal function to remove a token ID from the list of a given address
            * @param _from address representing the previous owner of the given token ID
            * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
            */
            function removeTokenFrom(address _from, uint256 _tokenId) internal {
                require (ownerOf(_tokenId) == _from);
                ownedTokensCount[_from] = ownedTokensCount[_from].sub(1);
                tokenOwner[_tokenId] = address(0);
            }
        
            /**
            * @dev Internal function to invoke `onERC721Received` on a target address
            * @dev The call is not executed if the target address is not a contract
            * @param _from address representing the previous owner of the given token ID
            * @param _to target address that will receive the tokens
            * @param _tokenId uint256 ID of the token to be transferred
            * @param _data bytes optional data to send along with the call
            * @return whether the call correctly returned the expected magic value
            */
            function checkAndCallSafeTransfer(
                address _from,
                address _to,
                uint256 _tokenId,
                bytes _data
            )
                internal
                returns (bool)
            {
                if (!_to.isContract()) {
                    return true;
                }
                bytes4 retval = ERC721Receiver(_to).onERC721Received(
                    msg.sender, _from, _tokenId, _data);
                return (retval == ERC721_RECEIVED);
            }
        }
        
        /**
         * @title ERC721 token receiver interface
         * @dev Interface for any contract that wants to support safeTransfers
         *  from ERC721 asset contracts.
         */
        contract ERC721Receiver {
            /**
            * @dev Magic value to be returned upon successful reception of an NFT
            *  Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
            *  which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
            */
            bytes4 public constant ERC721_RECEIVED = 0x150b7a02;
        
            /**
            * @notice Handle the receipt of an NFT
            * @dev The ERC721 smart contract calls this function on the recipient
            *  after a `safetransfer`. This function MAY throw to revert and reject the
            *  transfer. This function MUST use 50,000 gas or less. Return of other
            *  than the magic value MUST result in the transaction being reverted.
            *  Note: the contract address is always the message sender.
            * @param _from The sending address
            * @param _tokenId The NFT identifier which is being transfered
            * @param _data Additional data with no specified format
            * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
            */
            function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes _data
            )
                public
                returns(bytes4);
        }
        
        contract ERC721Holder is ERC721Receiver {
            function onERC721Received(
                address,
                address,
                uint256,
                bytes
            ) 
                public
                returns(bytes4)
                {
                    return ERC721_RECEIVED;
                }
        }
        
        /**
         * @title Full ERC721 Token
         * This implementation includes all the required and some optional functionality of the ERC721 standard
         * Moreover, it includes approve all functionality using operator terminology
         * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
         */
        contract ERC721Token is ERC721, ERC721BasicToken {
        
            // Token name
            string internal name_;
        
            // Token symbol
            string internal symbol_;
        
            // Mapping from owner to list of owned token IDs
            mapping(address => uint256[]) internal ownedTokens;
        
            // Mapping from token ID to index of the owner tokens list
            mapping(uint256 => uint256) internal ownedTokensIndex;
        
            // Array with all token ids, used for enumeration
            uint256[] internal allTokens;
        
            // Mapping from token id to position in the allTokens array
            mapping(uint256 => uint256) internal allTokensIndex;
        
            // Base Server Address for Token MetaData URI
            string internal tokenURIBase;
        
            /**
            * @dev Returns an URI for a given token ID
            * @dev Throws if the token ID does not exist. May return an empty string.
            * @notice The user/developper needs to add the tokenID, in the end of URL, to 
            * use the URI and get all details. Ex. www.<apiURL>.com/token/<tokenID>
            * @param _tokenId uint256 ID of the token to query
            */
            function tokenURI(uint256 _tokenId) public view returns (string) {
                require (exists(_tokenId));
                return tokenURIBase;
            }
        
            /**
            * @dev Gets the token ID at a given index of the tokens list of the requested owner
            * @param _owner address owning the tokens list to be accessed
            * @param _index uint256 representing the index to be accessed of the requested tokens list
            * @return uint256 token ID at the given index of the tokens list owned by the requested address
            */
            function tokenOfOwnerByIndex(
                address _owner,
                uint256 _index
            )
                public
                view
                returns (uint256)
            {
                require (_index < balanceOf(_owner));
                return ownedTokens[_owner][_index];
            }
        
            /**
            * @dev Gets the total amount of tokens stored by the contract
            * @return uint256 representing the total amount of tokens
            */
            function totalSupply() public view returns (uint256) {
                return allTokens.length;
            }
        
            /**
            * @dev Gets the token ID at a given index of all the tokens in this contract
            * @dev Reverts if the index is greater or equal to the total number of tokens
            * @param _index uint256 representing the index to be accessed of the tokens list
            * @return uint256 token ID at the given index of the tokens list
            */
            function tokenByIndex(uint256 _index) public view returns (uint256) {
                require (_index < totalSupply());
                return allTokens[_index];
            }
        
        
            /**
            * @dev Internal function to set the token URI for a given token
            * @dev Reverts if the token ID does not exist
            * @param _uri string URI to assign
            */
            function _setTokenURIBase(string _uri) internal {
                tokenURIBase = _uri;
            }
        
            /**
            * @dev Internal function to add a token ID to the list of a given address
            * @param _to address representing the new owner of the given token ID
            * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
            */
            function addTokenTo(address _to, uint256 _tokenId) internal {
                super.addTokenTo(_to, _tokenId);
                uint256 length = ownedTokens[_to].length;
                ownedTokens[_to].push(_tokenId);
                ownedTokensIndex[_tokenId] = length;
            }
        
            /**
            * @dev Internal function to remove a token ID from the list of a given address
            * @param _from address representing the previous owner of the given token ID
            * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
            */
            function removeTokenFrom(address _from, uint256 _tokenId) internal {
                super.removeTokenFrom(_from, _tokenId);
        
                // To prevent a gap in the array, we store the last token in the index of the token to delete, and
                // then delete the last slot.
                uint256 tokenIndex = ownedTokensIndex[_tokenId];
                uint256 lastTokenIndex = ownedTokens[_from].length.sub(1);
                uint256 lastToken = ownedTokens[_from][lastTokenIndex];
        
                ownedTokens[_from][tokenIndex] = lastToken;
                // This also deletes the contents at the last position of the array
                ownedTokens[_from].length--;
        
                // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
                // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
                // the lastToken to the first position, and then dropping the element placed in the last position of the list
        
                ownedTokensIndex[_tokenId] = 0;
                ownedTokensIndex[lastToken] = tokenIndex;
            }
        
            /**
            * @dev Gets the token name
            * @return string representing the token name
            */
            function name() public view returns (string) {
                return name_;
            }
        
            /**
            * @dev Gets the token symbol
            * @return string representing the token symbol
            */
            function symbol() public view returns (string) {
                return symbol_;
            }
        
            /**
            * @dev Internal function to mint a new token
            * @dev Reverts if the given token ID already exists
            * @param _to address the beneficiary that will own the minted token
            * @param _tokenId uint256 ID of the token to be minted by the msg.sender
            */
            function _mint(address _to, uint256 _tokenId) internal {
                super._mint(_to, _tokenId);
        
                allTokensIndex[_tokenId] = allTokens.length;
                allTokens.push(_tokenId);
            }
        
            /**
            * @dev Internal function to burn a specific token
            * @dev Reverts if the token does not exist
            * @param _owner owner of the token to burn
            * @param _tokenId uint256 ID of the token being burned by the msg.sender
            */
            function _burn(address _owner, uint256 _tokenId) internal {
                super._burn(_owner, _tokenId);
        
                // Reorg all tokens array
                uint256 tokenIndex = allTokensIndex[_tokenId];
                uint256 lastTokenIndex = allTokens.length.sub(1);
                uint256 lastToken = allTokens[lastTokenIndex];
        
                allTokens[tokenIndex] = lastToken;
                allTokens[lastTokenIndex] = 0;
        
                allTokens.length--;
                allTokensIndex[_tokenId] = 0;
                allTokensIndex[lastToken] = tokenIndex;
            }
        
            bytes4 constant InterfaceSignature_ERC165 = 0x01ffc9a7;
            /*
            bytes4(keccak256('supportsInterface(bytes4)'));
            */
        
            bytes4 constant InterfaceSignature_ERC721Enumerable = 0x780e9d63;
            /*
            bytes4(keccak256('totalSupply()')) ^
            bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
            bytes4(keccak256('tokenByIndex(uint256)'));
            */
        
            bytes4 constant InterfaceSignature_ERC721Metadata = 0x5b5e139f;
            /*
            bytes4(keccak256('name()')) ^
            bytes4(keccak256('symbol()')) ^
            bytes4(keccak256('tokenURI(uint256)'));
            */
        
            bytes4 constant InterfaceSignature_ERC721 = 0x80ac58cd;
            /*
            bytes4(keccak256('balanceOf(address)')) ^
            bytes4(keccak256('ownerOf(uint256)')) ^
            bytes4(keccak256('approve(address,uint256)')) ^
            bytes4(keccak256('getApproved(uint256)')) ^
            bytes4(keccak256('setApprovalForAll(address,bool)')) ^
            bytes4(keccak256('isApprovedForAll(address,address)')) ^
            bytes4(keccak256('transferFrom(address,address,uint256)')) ^
            bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
            bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'));
            */
        
            bytes4 public constant InterfaceSignature_ERC721Optional =- 0x4f558e79;
            /*
            bytes4(keccak256('exists(uint256)'));
            */
        
            /**
            * @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165).
            * @dev Returns true for any standardized interfaces implemented by this contract.
            * @param _interfaceID bytes4 the interface to check for
            * @return true for any standardized interfaces implemented by this contract.
            */
            function supportsInterface(bytes4 _interfaceID) external view returns (bool)
            {
                return ((_interfaceID == InterfaceSignature_ERC165)
                || (_interfaceID == InterfaceSignature_ERC721)
                || (_interfaceID == InterfaceSignature_ERC721Enumerable)
                || (_interfaceID == InterfaceSignature_ERC721Metadata));
            }
        
            function implementsERC721() public pure returns (bool) {
                return true;
            }
        
        }
        /* Lucid Sight, Inc. ERC-721 Collectibles. 
         * @title LSNFT - Lucid Sight, Inc. Non-Fungible Token
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract LSNFT is ERC721Token {
          
          /*** EVENTS ***/
        
          /// @dev The Created event is fired whenever a new collectible comes into existence.
          event Created(address owner, uint256 tokenId);
          
          /*** DATATYPES ***/
          
          struct NFT {
            // The sequence of potential attributes a Collectible has and can provide in creation events. Used in Creation logic to spwan new Cryptos
            uint256 attributes;
        
            // Current Game Card identifier
            uint256 currentGameCardId;
        
            // MLB Game Identifier (if asset generated as a game reward)
            uint256 mlbGameId;
        
            // player orverride identifier
            uint256 playerOverrideId;
        
            // official MLB Player ID
            uint256 mlbPlayerId;
        
            // earnedBy : In some instances we may want to retroactively write which MLB player triggered
            // the event that created a Legendary Trophy. This optional field should be able to be written
            // to after generation if we determine an event was newsworthy enough
            uint256 earnedBy;
            
            // asset metadata
            uint256 assetDetails;
            
            // Attach/Detach Flag
            uint256 isAttached;
          }
        
          NFT[] allNFTs;
        
          function isLSNFT() public view returns (bool) {
            return true;
          }
        
          /// For creating NFT
          function _createNFT (
            uint256[5] _nftData,
            address _owner,
            uint256 _isAttached)
            internal
            returns(uint256) {
        
            NFT memory _lsnftObj = NFT({
                attributes : _nftData[1],
                currentGameCardId : 0,
                mlbGameId : _nftData[2],
                playerOverrideId : _nftData[3],
                assetDetails: _nftData[0],
                isAttached: _isAttached,
                mlbPlayerId: _nftData[4],
                earnedBy: 0
            });
        
            uint256 newLSNFTId = allNFTs.push(_lsnftObj) - 1;
        
            _mint(_owner, newLSNFTId);
            
            // Created event
            emit Created(_owner, newLSNFTId);
        
            return newLSNFTId;
          }
        
          /// @dev Gets attributes of NFT  
          function _getAttributesOfToken(uint256 _tokenId) internal returns(NFT) {
            NFT storage lsnftObj = allNFTs[_tokenId];  
            return lsnftObj;
          }
        
          function _approveForSale(address _owner, address _to, uint256 _tokenId) internal {
            address owner = ownerOf(_tokenId);
            require (_to != owner);
            require (_owner == owner || isApprovedForAll(owner, _owner));
        
            if (getApproved(_tokenId) != address(0) || _to != address(0)) {
                tokenApprovals[_tokenId] = _to;
                emit Approval(_owner, _to, _tokenId);
            }
          }
        }
        
        /** Controls state and access rights for contract functions
         * @title Operational Control
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         * Inspired and adapted from contract created by OpenZeppelin 
         * Ref: https://github.com/OpenZeppelin/zeppelin-solidity/
         */
        contract OperationalControl {
            /// Facilitates access & control for the game.
            /// Roles:
            ///  -The Managers (Primary/Secondary): Has universal control of all elements (No ability to withdraw)
            ///  -The Banker: The Bank can withdraw funds and adjust fees / prices.
            ///  -otherManagers: Contracts that need access to functions for gameplay
        
            /// @dev Emited when contract is upgraded
            event ContractUpgrade(address newContract);
        
            /// @dev The addresses of the accounts (or contracts) that can execute actions within each roles.
            address public managerPrimary;
            address public managerSecondary;
            address public bankManager;
        
            // Contracts that require access for gameplay
            mapping(address => uint8) public otherManagers;
        
            // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
            bool public paused = false;
        
            // @dev Keeps track whether the contract erroredOut. When that is true, most actions are blocked & refund can be claimed
            bool public error = false;
        
            /**
             * @dev Operation modifiers for limiting access only to Managers
             */
            modifier onlyManager() {
                require (msg.sender == managerPrimary || msg.sender == managerSecondary);
                _;
            }
        
            /**
             * @dev Operation modifiers for limiting access to only Banker
             */
            modifier onlyBanker() {
                require (msg.sender == bankManager);
                _;
            }
        
            /**
             * @dev Operation modifiers for any Operators
             */
            modifier anyOperator() {
                require (
                    msg.sender == managerPrimary ||
                    msg.sender == managerSecondary ||
                    msg.sender == bankManager ||
                    otherManagers[msg.sender] == 1
                );
                _;
            }
        
            /**
             * @dev        Operation modifier for any Other Manager
             */
            modifier onlyOtherManagers() {
                require (otherManagers[msg.sender] == 1);
                _;
            }
        
            /**
             * @dev Assigns a new address to act as the Primary Manager.
             * @param _newGM    New primary manager address
             */
            function setPrimaryManager(address _newGM) external onlyManager {
                require (_newGM != address(0));
        
                managerPrimary = _newGM;
            }
        
            /**
             * @dev Assigns a new address to act as the Secondary Manager.
             * @param _newGM    New Secondary Manager Address
             */
            function setSecondaryManager(address _newGM) external onlyManager {
                require (_newGM != address(0));
        
                managerSecondary = _newGM;
            }
        
            /**
             * @dev Assigns a new address to act as the Banker.
             * @param _newBK    New Banker Address
             */
            function setBanker(address _newBK) external onlyManager {
                require (_newBK != address(0));
        
                bankManager = _newBK;
            }
        
            /// @dev Assigns a new address to act as the Other Manager. (State = 1 is active, 0 is disabled)
            function setOtherManager(address _newOp, uint8 _state) external onlyManager {
                require (_newOp != address(0));
        
                otherManagers[_newOp] = _state;
            }
        
            /*** Pausable functionality adapted from OpenZeppelin ***/
        
            /// @dev Modifier to allow actions only when the contract IS NOT paused
            modifier whenNotPaused() {
                require (!paused);
                _;
            }
        
            /// @dev Modifier to allow actions only when the contract IS paused
            modifier whenPaused {
                require (paused);
                _;
            }
        
            /// @dev Modifier to allow actions only when the contract has Error
            modifier whenError {
                require (error);
                _;
            }
        
            /**
             * @dev Called by any Operator role to pause the contract.
             * Used only if a bug or exploit is discovered (Here to limit losses / damage)
             */
            function pause() external onlyManager whenNotPaused {
                paused = true;
            }
        
            /**
             * @dev Unpauses the smart contract. Can only be called by the Game Master
             */
            function unpause() public onlyManager whenPaused {
                // can't unpause if contract was upgraded
                paused = false;
            }
        
            /**
             * @dev Errors out the contract thus mkaing the contract non-functionable
             */
            function hasError() public onlyManager whenPaused {
                error = true;
            }
        
            /**
             * @dev Removes the Error Hold from the contract and resumes it for working
             */
            function noError() public onlyManager whenPaused {
                error = false;
            }
        }
        
        /** Base contract for MLBNFT Collectibles. Holds all commons, events and base variables.
         * @title Lucid Sight MLB NFT 2018
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract CollectibleBase is LSNFT {
        
            /*** EVENTS ***/
        
            /// @dev Event emitted when an attribute of the player is updated
            event AssetUpdated(uint256 tokenId);
        
            /*** STORAGE ***/
        
            /// @dev A mapping of Team Id to Team Sequence Number to Collectible
            mapping (uint256 => mapping (uint32 => uint256) ) public nftTeamIdToSequenceIdToCollectible;
        
            /// @dev A mapping from Team IDs to the Sequqence Number .
            mapping (uint256 => uint32) public nftTeamIndexToCollectibleCount;
        
            /// @dev Array to hold details on attachment for each LS NFT Collectible
            mapping(uint256 => uint256[]) public nftCollectibleAttachments;
        
            /// @dev Mapping to control the asset generation per season.
            mapping(uint256 => uint256) public generationSeasonController;
        
            /// @dev Mapping for generation Season Dict.
            mapping(uint256 => uint256) public generationSeasonDict;
        
            /// @dev internal function to update player override id
            function _updatePlayerOverrideId(uint256 _tokenId, uint256 _newPlayerOverrideId) internal {
        
                // Get Token Obj
                NFT storage lsnftObj = allNFTs[_tokenId];
                lsnftObj.playerOverrideId = _newPlayerOverrideId;
        
                // Update Token Data with new updated attributes
                allNFTs[_tokenId] = lsnftObj;
        
                emit AssetUpdated(_tokenId);
            }
        
            /**
             * @dev An internal method that helps in generation of new NFT Collectibles
             * @param _teamId           teamId of the asset/token/collectible
             * @param _attributes       attributes of asset/token/collectible
             * @param _owner            owner of asset/token/collectible
             * @param _isAttached       State of the asset (attached or dettached)
             * @param _nftData          Array of data required for creation
             */
            function _createNFTCollectible(
                uint8 _teamId,
                uint256 _attributes,
                address _owner,
                uint256 _isAttached,
                uint256[5] _nftData
            )
                internal
                returns (uint256)
            {
                uint256 generationSeason = (_attributes % 1000000).div(1000);
                require (generationSeasonController[generationSeason] == 1);
        
                uint32 _sequenceId = getSequenceId(_teamId);
        
                uint256 newNFTCryptoId = _createNFT(_nftData, _owner, _isAttached);
                
                nftTeamIdToSequenceIdToCollectible[_teamId][_sequenceId] = newNFTCryptoId;
                nftTeamIndexToCollectibleCount[_teamId] = _sequenceId;
        
                return newNFTCryptoId;
            }
            
            function getSequenceId(uint256 _teamId) internal returns (uint32) {
                return (nftTeamIndexToCollectibleCount[_teamId] + 1);
            }
        
            /**
             * @dev Internal function, Helps in updating the Creation Stop Time
             * @param _season    Season UINT Code
             * @param _value    0 - Not allowed, 1 - Allowed
             */
            function _updateGenerationSeasonFlag(uint256 _season, uint8 _value) internal {
                generationSeasonController[_season] = _value;
            }
        
            /** @param _owner The owner whose ships tokens we are interested in.
              * @dev This method MUST NEVER be called by smart contract code. First, it's fairly
              *  expensive (it walks the entire Collectibles owners array looking for NFT belonging to owner)
            */      
            function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
                uint256 tokenCount = balanceOf(_owner);
        
                if (tokenCount == 0) {
                    // Return an empty array
                    return new uint256[](0);
                } else {
                    uint256[] memory result = new uint256[](tokenCount);
                    uint256 totalItems = balanceOf(_owner);
                    uint256 resultIndex = 0;
        
                    // We count on the fact that all Collectible have IDs starting at 0 and increasing
                    // sequentially up to the total count.
                    uint256 _assetId;
        
                    for (_assetId = 0; _assetId < totalItems; _assetId++) {
                        result[resultIndex] = tokenOfOwnerByIndex(_owner,_assetId);
                        resultIndex++;
                    }
        
                    return result;
                }
            }
        
            /// @dev internal function to update MLB player id
            function _updateMLBPlayerId(uint256 _tokenId, uint256 _newMLBPlayerId) internal {
        
                // Get Token Obj
                NFT storage lsnftObj = allNFTs[_tokenId];
                
                lsnftObj.mlbPlayerId = _newMLBPlayerId;
        
                // Update Token Data with new updated attributes
                allNFTs[_tokenId] = lsnftObj;
        
                emit AssetUpdated(_tokenId);
            }
        
            /// @dev internal function to update asset earnedBy value for an asset/token
            function _updateEarnedBy(uint256 _tokenId, uint256 _earnedBy) internal {
        
                // Get Token Obj
                NFT storage lsnftObj = allNFTs[_tokenId];
                
                lsnftObj.earnedBy = _earnedBy;
        
                // Update Token Data with new updated attributes
                allNFTs[_tokenId] = lsnftObj;
        
                emit AssetUpdated(_tokenId);
            }
        }
        
        /* Handles creating new Collectibles for promo and seed.
         * @title CollectibleMinting Minting
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         * Inspired and adapted from KittyCore.sol created by Axiom Zen
         * Ref: ETH Contract - 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d
         */
        contract CollectibleMinting is CollectibleBase, OperationalControl {
        
            uint256 public rewardsRedeemed = 0;
        
            /// @dev Counts the number of promo collectibles that can be made per-team
            uint256[31]  public promoCreatedCount;
            
            /// @dev Counts the number of seed collectibles that can be made in total
            uint256 public seedCreatedCount;
        
            /// @dev Bool to toggle batch support
            bool public isBatchSupported = true;
            
            /// @dev A mapping of contracts that can trigger functions
            mapping (address => bool) public contractsApprovedList;
            
            /**
             * @dev        Helps to toggle batch supported flag
             * @param      _flag  The flag
             */
            function updateBatchSupport(bool _flag) public onlyManager {
                isBatchSupported = _flag;
            }
        
            modifier canCreate() { 
                require (contractsApprovedList[msg.sender] || 
                    msg.sender == managerPrimary ||
                    msg.sender == managerSecondary); 
                _; 
            }
            
            /**
             * @dev Add an address to the Approved List
             * @param _newAddress   The new address to be approved for interaction with the contract
             */
            function addToApproveList(address _newAddress) public onlyManager {
                
                require (!contractsApprovedList[_newAddress]);
                contractsApprovedList[_newAddress] = true;
            }
        
            /**
             * @dev Remove an address from Approved List
             * @param _newAddress   The new address to be approved for interaction with the contract
             */
            function removeFromApproveList(address _newAddress) public onlyManager {
                require (contractsApprovedList[_newAddress]);
                delete contractsApprovedList[_newAddress];
            }
        
            
            /**
             * @dev Generates promo collectibles. Only callable by Game Master, with isAttached as 0.
             * @notice The generation of an asset if limited via the generationSeasonController
             * @param _teamId           teamId of the asset/token/collectible
             * @param _posId            position of the asset/token/collectible
             * @param _attributes       attributes of asset/token/collectible
             * @param _owner            owner of asset/token/collectible
             * @param _gameId          mlb game Identifier
             * @param _playerOverrideId player override identifier
             * @param _mlbPlayerId      official mlb player identifier
             */
            function createPromoCollectible(
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                address _owner,
                uint256 _gameId,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId)
                external
                canCreate
                whenNotPaused
                returns (uint256)
                {
        
                address nftOwner = _owner;
                if (nftOwner == address(0)) {
                     nftOwner = managerPrimary;
                }
        
                if(allNFTs.length > 0) {
                    promoCreatedCount[_teamId]++;
                }
                
                uint32 _sequenceId = getSequenceId(_teamId);
                
                uint256 assetDetails = uint256(uint64(now));
                assetDetails |= uint256(_sequenceId)<<64;
                assetDetails |= uint256(_teamId)<<96;
                assetDetails |= uint256(_posId)<<104;
        
                uint256[5] memory _nftData = [assetDetails, _attributes, _gameId, _playerOverrideId, _mlbPlayerId];
                
                return _createNFTCollectible(_teamId, _attributes, nftOwner, 0, _nftData);
            }
        
            /**
             * @dev Generaes a new single seed Collectible, with isAttached as 0.
             * @notice Helps in creating seed collectible.The generation of an asset if limited via the generationSeasonController
             * @param _teamId           teamId of the asset/token/collectible
             * @param _posId            position of the asset/token/collectible
             * @param _attributes       attributes of asset/token/collectible
             * @param _owner            owner of asset/token/collectible
             * @param _gameId          mlb game Identifier
             * @param _playerOverrideId player override identifier
             * @param _mlbPlayerId      official mlb player identifier
             */
            function createSeedCollectible(
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                address _owner,
                uint256 _gameId,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId)
                external
                canCreate
                whenNotPaused
                returns (uint256) {
        
                address nftOwner = _owner;
                
                if (nftOwner == address(0)) {
                     nftOwner = managerPrimary;
                }
                
                seedCreatedCount++;
                uint32 _sequenceId = getSequenceId(_teamId);
                
                uint256 assetDetails = uint256(uint64(now));
                assetDetails |= uint256(_sequenceId)<<64;
                assetDetails |= uint256(_teamId)<<96;
                assetDetails |= uint256(_posId)<<104;
        
                uint256[5] memory _nftData = [assetDetails, _attributes, _gameId, _playerOverrideId, _mlbPlayerId];
                
                return _createNFTCollectible(_teamId, _attributes, nftOwner, 0, _nftData);
            }
        
            /**
             * @dev Generate new Reward Collectible and transfer it to the owner, with isAttached as 0.
             * @notice Helps in redeeming the Rewards using our Oracle. Creates & transfers the asset to the redeemer (_owner)
             * The generation of an asset if limited via the generationSeasonController
             * @param _teamId           teamId of the asset/token/collectible
             * @param _posId            position of the asset/token/collectible
             * @param _attributes       attributes of asset/token/collectible
             * @param _owner            owner (redeemer) of asset/token/collectible
             * @param _gameId           mlb game Identifier
             * @param _playerOverrideId player override identifier
             * @param _mlbPlayerId      official mlb player identifier
             */
            function createRewardCollectible (
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                address _owner,
                uint256 _gameId,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId)
                external
                canCreate
                whenNotPaused
                returns (uint256) {
        
                address nftOwner = _owner;
                
                if (nftOwner == address(0)) {
                     nftOwner = managerPrimary;
                }
                
                rewardsRedeemed++;
                uint32 _sequenceId = getSequenceId(_teamId);
                
                uint256 assetDetails = uint256(uint64(now));
                assetDetails |= uint256(_sequenceId)<<64;
                assetDetails |= uint256(_teamId)<<96;
                assetDetails |= uint256(_posId)<<104;
        
                uint256[5] memory _nftData = [assetDetails, _attributes, _gameId, _playerOverrideId, _mlbPlayerId];
                
                return _createNFTCollectible(_teamId, _attributes, nftOwner, 0, _nftData);
            }
        
            /**
             * @dev Generate new ETH Card Collectible, with isAttached as 2.
             * @notice Helps to generate Collectibles/Tokens/Asset and transfer to ETH Cards,
             * which can be redeemed using our web-app.The generation of an asset if limited via the generationSeasonController
             * @param _teamId           teamId of the asset/token/collectible
             * @param _posId            position of the asset/token/collectible
             * @param _attributes       attributes of asset/token/collectible
             * @param _owner            owner of asset/token/collectible
             * @param _gameId           mlb game Identifier
             * @param _playerOverrideId player override identifier
             * @param _mlbPlayerId      official mlb player identifier
             */
            function createETHCardCollectible (
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                address _owner,
                uint256 _gameId,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId)
                external
                canCreate
                whenNotPaused
                returns (uint256) {
        
                address nftOwner = _owner;
                
                if (nftOwner == address(0)) {
                     nftOwner = managerPrimary;
                }
                
                rewardsRedeemed++;
                uint32 _sequenceId = getSequenceId(_teamId);
                
                uint256 assetDetails = uint256(uint64(now));
                assetDetails |= uint256(_sequenceId)<<64;
                assetDetails |= uint256(_teamId)<<96;
                assetDetails |= uint256(_posId)<<104;
        
                uint256[5] memory _nftData = [assetDetails, _attributes, _gameId, _playerOverrideId, _mlbPlayerId];
                
                return _createNFTCollectible(_teamId, _attributes, nftOwner, 2, _nftData);
            }
        }
        
        /* @title Interface for MLBNFT Contract
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract SaleManager {
            function createSale(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, address _owner) external;
        }
        
        /**
         * MLBNFT manages all aspects of the Lucid Sight, Inc. CryptoBaseball.
         * @title MLBNFT
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract MLBNFT is CollectibleMinting {
            
            /// @dev Set in case the MLBNFT contract requires an upgrade
            address public newContractAddress;
        
            string public constant MLB_Legal = "Major League Baseball trademarks and copyrights are used with permission of the applicable MLB entity.  All rights reserved.";
        
            // Time LS Oracle has to respond to detach requests
            uint32 public detachmentTime = 0;
        
            // Indicates if attached system is Active (Transfers will be blocked if attached and active)
            bool public attachedSystemActive;
        
            // Sale Manager Contract
            SaleManager public saleManagerAddress;
        
            /**
             * @dev MLBNFT constructor.
             */
            constructor() public {
                // Starts paused.
                paused = true;
                managerPrimary = msg.sender;
                managerSecondary = msg.sender;
                bankManager = msg.sender;
                name_ = "LucidSight-MLB-NFT";
                symbol_ = "MLBCB";
            }
        
            /**
             * @dev        Sets the address for the NFT Contract
             * @param      _saleManagerAddress  The nft address
             */
            function setSaleManagerAddress(address _saleManagerAddress) public onlyManager {
                require (_saleManagerAddress != address(0));
                saleManagerAddress = SaleManager(_saleManagerAddress);
            }
        
            /**
            * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
            * @param _tokenId uint256 ID of the token to validate
            */
            modifier canTransfer(uint256 _tokenId) {
                uint256 isAttached = checkIsAttached(_tokenId);
                if(isAttached == 2) {
                    //One-Time Auth for Physical Card Transfers
                    require (msg.sender == managerPrimary ||
                        msg.sender == managerSecondary ||
                        msg.sender == bankManager ||
                        otherManagers[msg.sender] == 1
                    );
                    updateIsAttached(_tokenId, 0);
                } else if(attachedSystemActive == true && isAttached >= 1) {
                    require (msg.sender == managerPrimary ||
                        msg.sender == managerSecondary ||
                        msg.sender == bankManager ||
                        otherManagers[msg.sender] == 1
                    );
                }
                else {
                    require (isApprovedOrOwner(msg.sender, _tokenId));
                }
            _;
            }
        
            /**
             * @dev Used to mark the smart contract as upgraded, in case of a issue
             * @param _v2Address    The new contract address
             */
            function setNewAddress(address _v2Address) external onlyManager {
                require (_v2Address != address(0));
                newContractAddress = _v2Address;
                emit ContractUpgrade(_v2Address);
            }
        
            /**
             * @dev Returns all the relevant information about a specific Collectible.
             * @notice Get details about your collectible
             * @param _tokenId              The token identifier
             * @return isAttached           Is Object attached
             * @return teamId               team identifier of the asset/token/collectible
             * @return positionId           position identifier of the asset/token/collectible
             * @return creationTime         creation timestamp
             * @return attributes           attribute of the asset/token/collectible
             * @return currentGameCardId    current game card of the asset/token/collectible
             * @return mlbGameID            mlb game identifier in which the asset/token/collectible was generated
             * @return playerOverrideId     player override identifier of the asset/token/collectible
             * @return playerStatus         status of the player (Rookie/Veteran/Historical)
             * @return playerHandedness     handedness of the asset
             * @return mlbPlayerId          official MLB Player Identifier
             */
            function getCollectibleDetails(uint256 _tokenId)
                external
                view
                returns (
                uint256 isAttached,
                uint32 sequenceId,
                uint8 teamId,
                uint8 positionId,
                uint64 creationTime,
                uint256 attributes,
                uint256 playerOverrideId,
                uint256 mlbGameId,
                uint256 currentGameCardId,
                uint256 mlbPlayerId,
                uint256 earnedBy,
                uint256 generationSeason
                ) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                
                attributes = obj.attributes;
                currentGameCardId = obj.currentGameCardId;
                mlbGameId = obj.mlbGameId;
                playerOverrideId = obj.playerOverrideId;
                mlbPlayerId = obj.mlbPlayerId;
        
                creationTime = uint64(obj.assetDetails);
                sequenceId = uint32(obj.assetDetails>>64);
                teamId = uint8(obj.assetDetails>>96);
                positionId = uint8(obj.assetDetails>>104);
                isAttached = obj.isAttached;
                earnedBy = obj.earnedBy;
        
                generationSeason = generationSeasonDict[(obj.attributes % 1000000) / 1000];
            }
        
            
            /**
             * @dev This is public rather than external so we can call super.unpause
             * without using an expensive CALL.
             */
            function unpause() public onlyManager {
                /// Actually unpause the contract.
                super.unpause();
            }
        
            /**
             * @dev Helper function to get the teamID of a collectible.To avoid using getCollectibleDetails
             * @notice Returns the teamID associated with the asset/collectible/token
             * @param _tokenId  The token identifier
             */
            function getTeamId(uint256 _tokenId) external view returns (uint256) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
        
                uint256 teamId = uint256(uint8(obj.assetDetails>>96));
                return uint256(teamId);
            }
        
            /**
             * @dev Helper function to get the position of a collectible.To avoid using getCollectibleDetails
             * @notice Returns the position of the asset/collectible/token
             * @param _tokenId  The token identifier
             */
            function getPositionId(uint256 _tokenId) external view returns (uint256) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
        
                uint256 positionId = uint256(uint8(obj.assetDetails>>104));
        
                return positionId;
            }
        
            /**
             * @dev Helper function to get the game card. To avoid using getCollectibleDetails
             * @notice Returns the gameCard associated with the asset/collectible/token
             * @param _tokenId  The token identifier
             */
            function getGameCardId(uint256 _tokenId) public view returns (uint256) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                return obj.currentGameCardId;
            }
        
            /**
             * @dev Returns isAttached property value for an asset/collectible/token
             * @param _tokenId  The token identifier
             */
            function checkIsAttached(uint256 _tokenId) public view returns (uint256) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                return obj.isAttached;
            }
        
            /**
             * @dev Helper function to get the attirbute of the collectible.To avoid using getCollectibleDetails
             * @notice Returns the ability of an asset/collectible/token from attributes.
             * @param _tokenId  The token identifier
             * @return ability  ability of the asset
             */
            function getAbilitiesForCollectibleId(uint256 _tokenId) external view returns (uint256 ability) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                uint256 _attributes = uint256(obj.attributes);
                ability = (_attributes % 1000);
            }
        
            /**
             * @dev Only allows trasnctions to go throught if the msg.sender is in the apporved list
             * @notice Updates the gameCardID properrty of the asset
             * @param _gameCardNumber  The game card number
             * @param _playerId        The player identifier
             */
            function updateCurrentGameCardId(uint256 _gameCardNumber, uint256 _playerId) public whenNotPaused {
                require (contractsApprovedList[msg.sender]);
        
                NFT memory obj  = _getAttributesOfToken(_playerId);
                
                obj.currentGameCardId = _gameCardNumber;
                
                if ( _gameCardNumber == 0 ) {
                    obj.isAttached = 0;
                } else {
                    obj.isAttached = 1;
                }
        
                allNFTs[_playerId] = obj;
            }
        
            /**
             * @dev Only Manager can add an attachment (special events) to the collectible
             * @notice Adds an attachment to collectible.
             * @param _tokenId  The token identifier
             * @param _attachment  The attachment
             */
            function addAttachmentToCollectible ( 
                uint256 _tokenId,
                uint256 _attachment)
                external
                onlyManager
                whenNotPaused {
                require (exists(_tokenId));
        
                nftCollectibleAttachments[_tokenId].push(_attachment);
                emit AssetUpdated(_tokenId);
            }
        
            /**
             * @dev It will remove the attachment form the collectible. We will need to re-add all attachment(s) if removed.
             * @notice Removes all attachments from collectible.
             * @param _tokenId  The token identifier
             */
            function removeAllAttachmentsFromCollectible(uint256 _tokenId)
                external
                onlyManager
                whenNotPaused {
        
                require (exists(_tokenId));
                
                delete nftCollectibleAttachments[_tokenId];
                emit AssetUpdated(_tokenId);
            }
        
            /**
             * @notice Transfers the ownership of NFT from one address to another address
             * @dev responsible for gifting assets to other user.
             * @param _to       to address
             * @param _tokenId  The token identifier
             */
            function giftAsset(address _to, uint256 _tokenId) public whenNotPaused {        
                safeTransferFrom(msg.sender, _to, _tokenId);
            }
            
            /**
             * @dev responsible for setting the tokenURI.
             * @notice The user/developper needs to add the tokenID, in the end of URL, to 
             * use the URI and get all details. Ex. www.<apiURL>.com/token/<tokenID>
             * @param _tokenURI  The token uri
             */
            function setTokenURIBase (string _tokenURI) public anyOperator {
                _setTokenURIBase(_tokenURI);
            }
        
            /**
             * @dev Allowed to be called by onlyGameManager to update a certain collectible playerOverrideID
             * @notice Sets the player override identifier.
             * @param _tokenId      The token identifier
             * @param _newOverrideId     The new player override identifier
             */
            function setPlayerOverrideId(uint256 _tokenId, uint256 _newOverrideId) public onlyManager whenNotPaused {
                require (exists(_tokenId));
        
                _updatePlayerOverrideId(_tokenId, _newOverrideId);
            }
        
            /**
             * @notice Updates the Generation Season Controller.
             * @dev Allowed to be called by onlyGameManager to update the generation season.
             * this helps to control the generation of collectible.
             * @param _season    Season UINT representation
             * @param _value    0-Not allowed, 1-open, >=2 Locked Forever
             */
            function updateGenerationStopTime(uint256 _season, uint8 _value ) public  onlyManager whenNotPaused {
                require (generationSeasonController[_season] == 1 && _value != 0);
                _updateGenerationSeasonFlag(_season, _value);
            }
        
            /**
             * @dev set Generation Season Controller, can only be called by Managers._season can be [0,1,2,3..] and 
             * _value can be [0,1,N].
             * @notice _value of 1: means generation of collectible is allowed. anything, apart from 1, wont allow generating assets for that season.
             * @param _season    Season UINT representation
             */
            function setGenerationSeasonController(uint256 _season) public onlyManager whenNotPaused {
                require (generationSeasonController[_season] == 0);
                _updateGenerationSeasonFlag(_season, 1);
            }
        
            /**
             * @dev Adding value to DICT helps in showing the season value in getCollectibleDetails
             * @notice Updates the Generation Season Dict.
             * @param _season    Season UINT representation
             * @param _value    0-Not allowed,1-allowed
             */
            function updateGenerationDict(uint256 _season, uint64 _value) public onlyManager whenNotPaused {
                require (generationSeasonDict[_season] <= 1);
                generationSeasonDict[_season] = _value;
            }
        
            /**
             * @dev Helper function to avoid calling getCollectibleDetails
             * @notice Gets the MLB player Id from the player attributes
             * @param _tokenId  The token identifier
             * @return playerId  MLB Player Identifier
             */
            function getPlayerId(uint256 _tokenId) external view returns (uint256 playerId) {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                playerId = ((obj.attributes.div(100000000000000000)) % 1000);
            }
            
            /**
             * @dev Helper function to avoid calling getCollectibleDetails
             * @notice Gets the attachments for an asset
             * @param _tokenId  The token identifier
             * @return attachments
             */
            function getAssetAttachment(uint256 _tokenId) external view returns (uint256[]) {
                uint256[] _attachments = nftCollectibleAttachments[_tokenId];
                uint256[] attachments;
                for(uint i=0;i<_attachments.length;i++){
                    attachments.push(_attachments[i]);
                }
                
                return attachments;
            }
        
            /**
             * @dev Can only be trigerred by Managers. Updates the earnedBy property of the NFT
             * @notice Helps in updating the earned _by property of an asset/token.
             * @param  _tokenId        asser/token identifier
             * @param  _earnedBy       New asset/token DNA
             */
            function updateEarnedBy(uint256 _tokenId, uint256 _earnedBy) public onlyManager whenNotPaused {
                require (exists(_tokenId));
        
                _updateEarnedBy(_tokenId, _earnedBy);
            }
        
            /**
             * @dev A batch function to facilitate batching of asset creation. canCreate modifier
             * helps in controlling who can call the function
             * @notice Batch Function to Create Assets
             * @param      _teamId            The team identifier
             * @param      _attributes        The attributes
             * @param      _playerOverrideId  The player override identifier
             * @param      _mlbPlayerId       The mlb player identifier
             * @param      _to                To Address
             */
            function batchCreateAsset(
                uint8[] _teamId,
                uint256[] _attributes,
                uint256[] _playerOverrideId,
                uint256[] _mlbPlayerId,
                address[] _to)
                external
                canCreate
                whenNotPaused {
                    require (isBatchSupported);
        
                    require (_teamId.length > 0 && _attributes.length > 0 && 
                        _playerOverrideId.length > 0 && _mlbPlayerId.length > 0 && 
                        _to.length > 0);
        
                    uint256 assetDetails;
                    uint256[5] memory _nftData;
                    
                    for(uint ii = 0; ii < _attributes.length; ii++){
                        require (_to[ii] != address(0) && _teamId[ii] != 0 && _attributes.length != 0 && 
                            _mlbPlayerId[ii] != 0);
                        
                        assetDetails = uint256(uint64(now));
                        assetDetails |= uint256(getSequenceId(_teamId[ii]))<<64;
                        assetDetails |= uint256(_teamId[ii])<<96;
                        assetDetails |= uint256((_attributes[ii]/1000000000000000000000000000000000000000)-800)<<104;
                
                        _nftData = [assetDetails, _attributes[ii], 0, _playerOverrideId[ii], _mlbPlayerId[ii]];
                        
                        _createNFTCollectible(_teamId[ii], _attributes[ii], _to[ii], 0, _nftData);
                    }
                }
        
            /**
             * @dev A batch function to facilitate batching of asset creation for ETH Cards. canCreate modifier
             * helps in controlling who can call the function
             * @notice        Batch Function to Create Assets
             * @param      _teamId            The team identifier
             * @param      _attributes        The attributes
             * @param      _playerOverrideId  The player override identifier
             * @param      _mlbPlayerId       The mlb player identifier
             * @param      _to                { parameter_description }
             */
            function batchCreateETHCardAsset(
                uint8[] _teamId,
                uint256[] _attributes,
                uint256[] _playerOverrideId,
                uint256[] _mlbPlayerId,
                address[] _to)
                external
                canCreate
                whenNotPaused {
                    require (isBatchSupported);
        
                    require (_teamId.length > 0 && _attributes.length > 0
                                && _playerOverrideId.length > 0 &&
                                _mlbPlayerId.length > 0 && _to.length > 0);
        
                    uint256 assetDetails;
                    uint256[5] memory _nftData;
        
                    for(uint ii = 0; ii < _attributes.length; ii++){
        
                        require (_to[ii] != address(0) && _teamId[ii] != 0 && _attributes.length != 0 && 
                            _mlbPlayerId[ii] != 0);
                
                        assetDetails = uint256(uint64(now));
                        assetDetails |= uint256(getSequenceId(_teamId[ii]))<<64;
                        assetDetails |= uint256(_teamId[ii])<<96;
                        assetDetails |= uint256((_attributes[ii]/1000000000000000000000000000000000000000)-800)<<104;
                
                        _nftData = [assetDetails, _attributes[ii], 0, _playerOverrideId[ii], _mlbPlayerId[ii]];
                        
                        _createNFTCollectible(_teamId[ii], _attributes[ii], _to[ii], 2, _nftData);
                    }
                }
        
            /**
             * @dev        Overriden TransferFrom, with the modifier canTransfer which uses our attachment system
             * @notice     Helps in trasnferring assets
             * @param      _from     the address sending from
             * @param      _to       the address sending to
             * @param      _tokenId  The token identifier
             */
            function transferFrom(
                address _from,
                address _to,
                uint256 _tokenId
            )
                public
                canTransfer(_tokenId)
            {
                // Asset should not be in play
                require (checkIsAttached(_tokenId) == 0);
                
                require (_from != address(0));
        
                require (_to != address(0));
        
                clearApproval(_from, _tokenId);
                removeTokenFrom(_from, _tokenId);
                addTokenTo(_to, _tokenId);
        
                emit Transfer(_from, _to, _tokenId);
            }
        
            /**
             * @dev     Facilitates batch trasnfer of collectible with multiple TO Address, depending if batch is supported on contract.
             * @notice  Batch Trasnfer with multpple TO addresses
             * @param      _tokenIds  The token identifiers
             * @param      _fromB     the address sending from
             * @param      _toB       the address sending to
             */
            function multiBatchTransferFrom(
                uint256[] _tokenIds, 
                address[] _fromB, 
                address[] _toB) 
                public
            {
                require (isBatchSupported);
        
                require (_tokenIds.length > 0 && _fromB.length > 0 && _toB.length > 0);
        
                uint256 _id;
                address _to;
                address _from;
                
                for (uint256 i = 0; i < _tokenIds.length; ++i) {
        
                    require (_tokenIds[i] != 0 && _fromB[i] != 0 && _toB[i] != 0);
        
                    _id = _tokenIds[i];
                    _to = _toB[i];
                    _from = _fromB[i];
        
                    transferFrom(_from, _to, _id);
                }
                
            }
            
            /**
             * @dev     Facilitates batch trasnfer of collectible, depending if batch is supported on contract
             * @notice        Batch TransferFrom with the same to & from address
             * @param      _tokenIds  The asset identifiers
             * @param      _from      the address sending from
             * @param      _to        the address sending to
             */
            function batchTransferFrom(uint256[] _tokenIds, address _from, address _to) 
                public
            {
                require (isBatchSupported);
        
                require (_tokenIds.length > 0 && _from != address(0) && _to != address(0));
        
                uint256 _id;
                
                for (uint256 i = 0; i < _tokenIds.length; ++i) {
                    
                    require (_tokenIds[i] != 0);
        
                    _id = _tokenIds[i];
        
                    transferFrom(_from, _to, _id);
                }
            }
            
            /**
             * @dev     Facilitates batch trasnfer of collectible, depending if batch is supported on contract.
             * Checks for collectible 0,address 0 and then performs the transfer
             * @notice        Batch SafeTransferFrom with multiple From and to Addresses
             * @param      _tokenIds  The asset identifiers
             * @param      _fromB     the address sending from
             * @param      _toB       the address sending to
             */
            function multiBatchSafeTransferFrom(
                uint256[] _tokenIds, 
                address[] _fromB, 
                address[] _toB
                )
                public
            {
                require (isBatchSupported);
        
                require (_tokenIds.length > 0 && _fromB.length > 0 && _toB.length > 0);
        
                uint256 _id;
                address _to;
                address _from;
                
                for (uint256 i = 0; i < _tokenIds.length; ++i) {
        
                    require (_tokenIds[i] != 0 && _fromB[i] != 0 && _toB[i] != 0);
        
                    _id = _tokenIds[i];
                    _to  = _toB[i];
                    _from  = _fromB[i];
        
                    safeTransferFrom(_from, _to, _id);
                }
            }
        
            /**
             * @dev     Facilitates batch trasnfer of collectible, depending if batch is supported on contract.
             * Checks for collectible 0,address 0 and then performs the transfer
             * @notice        Batch SafeTransferFrom from a single address to another address
             * @param      _tokenIds  The asset identifiers
             * @param      _from     the address sending from
             * @param      _to       the address sending to
             */
            function batchSafeTransferFrom(
                uint256[] _tokenIds, 
                address _from, 
                address _to
                )
                public
            {   
                require (isBatchSupported);
        
                require (_tokenIds.length > 0 && _from != address(0) && _to != address(0));
        
                uint256 _id;
                for (uint256 i = 0; i < _tokenIds.length; ++i) {
                    require (_tokenIds[i] != 0);
                    _id = _tokenIds[i];
                    safeTransferFrom(_from, _to, _id);
                }
            }
        
            /**
             * @notice     Batch Function to approve the spender
             * @dev        Helps to approve a batch of collectibles 
             * @param      _tokenIds  The asset identifiers
             * @param      _spender   The spender
             */
            function batchApprove(
                uint256[] _tokenIds, 
                address _spender
                )
                public
            {   
                require (isBatchSupported);
        
                require (_tokenIds.length > 0 && _spender != address(0));
                
                uint256 _id;
                for (uint256 i = 0; i < _tokenIds.length; ++i) {
        
                    require (_tokenIds[i] != 0);
                    
                    _id = _tokenIds[i];
                    approve(_spender, _id);
                }
                
            }
        
            /**
             * @dev        Batch Function to mark spender for approved for all. Does a check
             * for address(0) and throws if true
             * @notice     Facilitates batch approveAll
             * @param      _spenders  The spenders
             * @param      _approved  The approved
             */
            function batchSetApprovalForAll(
                address[] _spenders,
                bool _approved
                )
                public
            {   
                require (isBatchSupported);
        
                require (_spenders.length > 0);
        
                address _spender;
                for (uint256 i = 0; i < _spenders.length; ++i) {        
        
                    require (address(_spenders[i]) != address(0));
                        
                    _spender = _spenders[i];
                    setApprovalForAll(_spender, _approved);
                }
            }  
            
            /**
             * @dev        Function to request Detachment from our Contract
             * @notice     a wallet can request to detach it collectible, so, that it can be used in other third-party contracts.
             * @param      _tokenId  The token identifier
             */
            function requestDetachment(
                uint256 _tokenId
            )
                public
            {
                //Request can only be made by owner or approved address
                require (isApprovedOrOwner(msg.sender, _tokenId));
        
                uint256 isAttached = checkIsAttached(_tokenId);
        
                //If collectible is on a gamecard prevent detachment
                require(getGameCardId(_tokenId) == 0);
        
                require (isAttached >= 1);
        
                if(attachedSystemActive == true) {
                    //Checks to see if request was made and if time elapsed
                    if(isAttached > 1 && block.timestamp - isAttached > detachmentTime) {
                        isAttached = 0;
                    } else if(isAttached > 1) {
                        //Forces Tx Fail if time is already set for attachment and not less than detachmentTime
                        require (isAttached == 1);
                    } else {
                        //Is attached, set detachment time and make request to detach
                        // emit AssetUpdated(_tokenId);
                        isAttached = block.timestamp;
                    }
                } else {
                    isAttached = 0;
                }
        
                updateIsAttached(_tokenId, isAttached);
            }
        
            /**
             * @dev        Function to attach the asset, thus, restricting transfer
             * @notice     Attaches the collectible to our contract
             * @param      _tokenId  The token identifier
             */
            function attachAsset(
                uint256 _tokenId
            )
                public
                canTransfer(_tokenId)
            {
                uint256 isAttached = checkIsAttached(_tokenId);
        
                require (isAttached == 0);
                isAttached = 1;
        
                updateIsAttached(_tokenId, isAttached);
        
                emit AssetUpdated(_tokenId);
            }
        
            /**
             * @dev        Batch attach function
             * @param      _tokenIds  The identifiers
             */
            function batchAttachAssets(uint256[] _tokenIds) public {
                require (isBatchSupported);
        
                for(uint i = 0; i < _tokenIds.length; i++) {
                    attachAsset(_tokenIds[i]);
                }
            }
        
            /**
             * @dev        Batch detach function
             * @param      _tokenIds  The identifiers
             */
            function batchDetachAssets(uint256[] _tokenIds) public {
                require (isBatchSupported);
        
                for(uint i = 0; i < _tokenIds.length; i++) {
                    requestDetachment(_tokenIds[i]);
                }
            }
        
            /**
             * @dev        Function to facilitate detachment when contract is paused
             * @param      _tokenId  The identifiers
             */
            function requestDetachmentOnPause (uint256 _tokenId) public whenPaused {
                //Request can only be made by owner or approved address
                require (isApprovedOrOwner(msg.sender, _tokenId));
        
                updateIsAttached(_tokenId, 0);
            }
        
            /**
             * @dev        Toggle the Attachment Switch
             * @param      _state  The state
             */
            function toggleAttachedEnforcement (bool _state) public onlyManager {
                attachedSystemActive = _state;
            }
        
            /**
             * @dev        Set Attachment Time Period (this restricts user from continuously trigger detachment)
             * @param      _time  The time
             */
            function setDetachmentTime (uint256 _time) public onlyManager {
                //Detactment Time can not be set greater than 2 weeks.
                require (_time <= 1209600);
                detachmentTime = uint32(_time);
            }
        
            /**
             * @dev        Detach Asset From System
             * @param      _tokenId  The token iddentifier
             */
            function setNFTDetached(uint256 _tokenId) public anyOperator {
                require (checkIsAttached(_tokenId) > 0);
        
                updateIsAttached(_tokenId, 0);
            }
        
            /**
             * @dev        Batch function to detach multiple assets
             * @param      _tokenIds  The token identifiers
             */
            function setBatchDetachCollectibles(uint256[] _tokenIds) public anyOperator {
                uint256 _id;
                for(uint i = 0; i < _tokenIds.length; i++) {
                    _id = _tokenIds[i];
                    setNFTDetached(_id);
                }
            }
        
            /**
             * @dev        Function to update attach value
             * @param      _tokenId     The asset id
             * @param      _isAttached  Indicates if attached
             */
            function updateIsAttached(uint256 _tokenId, uint256 _isAttached) internal {
                NFT memory obj  = _getAttributesOfToken(_tokenId);
                
                obj.isAttached = _isAttached;
            
                allNFTs[_tokenId] = obj;
                emit AssetUpdated(_tokenId);
            }
        
            /**
            * @dev   Facilitates Creating Sale using the Sale Contract. Forces owner check & collectibleId check
            * @notice Helps a wallet to create a sale using our Sale Contract
            * @param      _tokenId        The token identifier
            * @param      _startingPrice  The starting price
            * @param      _endingPrice    The ending price
            * @param      _duration       The duration
            */
            function initiateCreateSale(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration) external {
                require (_tokenId != 0);
                
                // If MLBNFT is already on any sale, this will throw
                // because it will be owned by the sale contract.
                address owner = ownerOf(_tokenId);
                require (owner == msg.sender);
        
                // Sale contract checks input sizes
                require (_startingPrice == _startingPrice);
                require (_endingPrice == _endingPrice);
                require (_duration == _duration);
        
                require (checkIsAttached(_tokenId) == 0);
                
                // One time approval for the tokenID
                _approveForSale(msg.sender, address(saleManagerAddress), _tokenId);
        
                saleManagerAddress.createSale(_tokenId, _startingPrice, _endingPrice, _duration, msg.sender);
            }
        
            /**
             * @dev        Facilitates batch auction of collectibles, and enforeces strict checking on the collectibleId,starting/ending price, duration.
             * @notice     Batch function to put 10 or less collectibles on sale
             * @param      _tokenIds        The token identifier
             * @param      _startingPrices  The starting price
             * @param      _endingPrices    The ending price
             * @param      _durations       The duration
             */
            function batchCreateAssetSale(uint256[] _tokenIds, uint256[] _startingPrices, uint256[] _endingPrices, uint256[] _durations) external whenNotPaused {
        
                require (_tokenIds.length > 0 && _startingPrices.length > 0 && _endingPrices.length > 0 && _durations.length > 0);
                
                // Sale contract checks input sizes
                for(uint ii = 0; ii < _tokenIds.length; ii++){
        
                    // Do not process for tokenId 0
                    require (_tokenIds[ii] != 0);
                    
                    require (_startingPrices[ii] == _startingPrices[ii]);
                    require (_endingPrices[ii] == _endingPrices[ii]);
                    require (_durations[ii] == _durations[ii]);
        
                    // If MLBNFT is already on any sale, this will throw
                    // because it will be owned by the sale contract.
                    address _owner = ownerOf(_tokenIds[ii]);
                    address _msgSender = msg.sender;
                    require (_owner == _msgSender);
        
                    // Check whether the collectible is inPlay. If inPlay cant put it on Sale
                    require (checkIsAttached(_tokenIds[ii]) == 0);
                    
                    // approve token to for Sale creation
                    _approveForSale(msg.sender, address(saleManagerAddress), _tokenIds[ii]);
                    
                    saleManagerAddress.createSale(_tokenIds[ii], _startingPrices[ii], _endingPrices[ii], _durations[ii], msg.sender);
                }
            }
        }

        File 2 of 2: SaleManager
        pragma solidity ^0.4.23;
        
        /**
         * @title SafeMath
         * @dev Math operations with safety checks that revert on error
         */
        library SafeMath {
        
          /**
          * @dev Multiplies two numbers, reverts on overflow.
          */
          function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
            if (_a == 0) {
              return 0;
            }
        
            uint256 c = _a * _b;
            require(c / _a == _b);
        
            return c;
          }
        
          /**
          * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
          */
          function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
            require(_b > 0); // Solidity only automatically asserts when dividing by 0
            uint256 c = _a / _b;
            // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
        
            return c;
          }
        
          /**
          * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
          */
          function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
            require(_b <= _a);
            uint256 c = _a - _b;
        
            return c;
          }
        
          /**
          * @dev Adds two numbers, reverts on overflow.
          */
          function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
            uint256 c = _a + _b;
            require(c >= _a);
        
            return c;
          }
        }
        
        /* Controls game play state and access rights for game functions
         * @title Operational Control
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         * Inspired and adapted from contract created by OpenZeppelin
         * Ref: https://github.com/OpenZeppelin/zeppelin-solidity/
         */
        contract OperationalControl {
            // Facilitates access & control for the game.
            // Roles:
            //  -The Game Managers (Primary/Secondary): Has universal control of all game elements (No ability to withdraw)
            //  -The Banker: The Bank can withdraw funds and adjust fees / prices.
        
            /// @dev Emited when contract is upgraded
            event ContractUpgrade(address newContract);
        
            mapping (address => bool) allowedAddressList;
            
        
            // The addresses of the accounts (or contracts) that can execute actions within each roles.
            address public gameManagerPrimary;
            address public gameManagerSecondary;
            address public bankManager;
        
            // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
            bool public paused = false;
        
            /// @dev Operation modifiers for limiting access
            modifier onlyGameManager() {
                require(msg.sender == gameManagerPrimary || msg.sender == gameManagerSecondary);
                _;
            }
        
            /// @dev Operation modifiers for limiting access to only Banker
            modifier onlyBanker() {
                require(msg.sender == bankManager);
                _;
            }
        
            /// @dev Operation modifiers for access to any Manager
            modifier anyOperator() {
                require(
                    msg.sender == gameManagerPrimary ||
                    msg.sender == gameManagerSecondary ||
                    msg.sender == bankManager
                );
                _;
            }
        
            /// @dev Assigns a new address to act as the GM.
            function setPrimaryGameManager(address _newGM) external onlyGameManager {
                require(_newGM != address(0));
        
                gameManagerPrimary = _newGM;
            }
        
            /// @dev Assigns a new address to act as the GM.
            function setSecondaryGameManager(address _newGM) external onlyGameManager {
                require(_newGM != address(0));
        
                gameManagerSecondary = _newGM;
            }
        
            /// @dev Assigns a new address to act as the Banker.
            function setBanker(address _newBK) external onlyBanker {
                require(_newBK != address(0));
        
                bankManager = _newBK;
            }
        
            function updateAllowedAddressesList (address _newAddress, bool _value) external onlyGameManager {
        
                require (_newAddress != address(0));
        
                allowedAddressList[_newAddress] = _value;
                
            }
        
            modifier canTransact() { 
                require (msg.sender == gameManagerPrimary
                    || msg.sender == gameManagerSecondary
                    || allowedAddressList[msg.sender]); 
                _; 
            }
            
        
            /*** Pausable functionality adapted from OpenZeppelin ***/
        
            /// @dev Modifier to allow actions only when the contract IS NOT paused
            modifier whenNotPaused() {
                require(!paused);
                _;
            }
        
            /// @dev Modifier to allow actions only when the contract IS paused
            modifier whenPaused {
                require(paused);
                _;
            }
        
            /// @dev Called by any Operator role to pause the contract.
            /// Used only if a bug or exploit is discovered (Here to limit losses / damage)
            function pause() external onlyGameManager whenNotPaused {
                paused = true;
            }
        
            /// @dev Unpauses the smart contract. Can only be called by the Game Master
            /// @notice This is public rather than external so it can be called by derived contracts. 
            function unpause() public onlyGameManager whenPaused {
                // can't unpause if contract was upgraded
                paused = false;
            }
        }
        
        /* @title Interface for MLBNFT Contract
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract MLBNFT {
            function exists(uint256 _tokenId) public view returns (bool _exists);
            function ownerOf(uint256 _tokenId) public view returns (address _owner);
            function approve(address _to, uint256 _tokenId) public;
            function setApprovalForAll(address _to, bool _approved) public;
            function transferFrom(address _from, address _to, uint256 _tokenId) public;
            function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;
            function createPromoCollectible(uint8 _teamId, uint8 _posId, uint256 _attributes, address _owner, uint256 _gameId, uint256 _playerOverrideId, uint256 _mlbPlayerId) external returns (uint256);
            function createSeedCollectible(uint8 _teamId, uint8 _posId, uint256 _attributes, address _owner, uint256 _gameId, uint256 _playerOverrideId, uint256 _mlbPlayerId) public returns (uint256);
            function checkIsAttached(uint256 _tokenId) public view returns (uint256);
            function getTeamId(uint256 _tokenId) external view returns (uint256);
            function getPlayerId(uint256 _tokenId) external view returns (uint256 playerId);
            function getApproved(uint256 _tokenId) public view returns (address _operator);
            function isApprovedForAll(address _owner, address _operator) public view returns (bool);
        }
        
        /* @title Interface for ETH Escrow Contract
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract LSEscrow {
            function escrowTransfer(address seller, address buyer, uint256 currentPrice, uint256 marketsCut) public returns(bool);
        }
        
        
        
        /**
         * @title ERC721 token receiver interface
         * @dev Interface for any contract that wants to support safeTransfers
         *  from ERC721 asset contracts.
         */
        contract ERC721Receiver {
            /**
            * @dev Magic value to be returned upon successful reception of an NFT
            *  Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
            *  which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
            */
            bytes4 public constant ERC721_RECEIVED = 0x150b7a02;
        
            /**
            * @notice Handle the receipt of an NFT
            * @dev The ERC721 smart contract calls this function on the recipient
            *  after a `safetransfer`. This function MAY throw to revert and reject the
            *  transfer. This function MUST use 50,000 gas or less. Return of other
            *  than the magic value MUST result in the transaction being reverted.
            *  Note: the contract address is always the message sender.
            * @param _from The sending address
            * @param _tokenId The NFT identifier which is being transfered
            * @param _data Additional data with no specified format
            * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
            */
            function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes _data
            )
                public
                returns(bytes4);
        }
        
        contract ERC721Holder is ERC721Receiver {
            function onERC721Received(address,address, uint256, bytes) public returns(bytes4) {
                return ERC721_RECEIVED;
            }
        }
        
        /* Contains models, variables, and internal methods for the ERC-721 sales.
         * @title Sale Base
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract SaleBase is OperationalControl, ERC721Holder {
            using SafeMath for uint256;
            
            /// EVENTS 
        
            event SaleCreated(uint256 tokenID, uint256 startingPrice, uint256 endingPrice, uint256 duration, uint256 startedAt);
            event TeamSaleCreated(uint256[9] tokenIDs, uint256 startingPrice, uint256 endingPrice, uint256 duration, uint256 startedAt);
            event SaleWinner(uint256 tokenID, uint256 totalPrice, address winner);
            event TeamSaleWinner(uint256[9] tokenIDs, uint256 totalPrice, address winner);
            event SaleCancelled(uint256 tokenID, address sellerAddress);
            event EtherWithdrawed(uint256 value);
        
            /// STORAGE
        
            /**
             * @dev        Represents an Sale on MLB CryptoBaseball (ERC721)
             */
            struct Sale {
                // Current owner of NFT (ERC721)
                address seller;
                // Price (in wei) at beginning of sale
                uint256 startingPrice;
                // Price (in wei) at end of sale
                uint256 endingPrice;
                // Duration (in seconds) of sale
                uint256 duration;
                // Time when sale started
                // NOTE: 0 if this sale has been concluded
                uint256 startedAt;
                // ERC721 AssetID
                uint256[9] tokenIds;
            }
        
            /**
             * @dev        Reference to contract tracking ownership & asset details
             */
            MLBNFT public nonFungibleContract;
        
            /**
             * @dev        Reference to contract tracking ownership & asset details
             */
            LSEscrow public LSEscrowContract;
        
            /**
             * @dev   Defining a GLOBAL delay time for the auctions to start accepting bidExcess
             * @notice This variable is made to delay the bid process.
             */
            uint256 public BID_DELAY_TIME = 0;
        
            // Cut owner takes on each sale, measured in basis points (1/100 of a percent).
            // Values 0-10,000 map to 0%-100%
            uint256 public ownerCut = 500; //5%
        
            // Map from token to their corresponding sale.
            mapping (uint256 => Sale) tokenIdToSale;
        
            /**
             * @dev        Returns true if the claimant owns the token.
             * @param      _claimant  The claimant
             * @param      _tokenId   The token identifier
             */
            function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
                return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
            }
        
            /**
             * @dev        Internal function to ESCROW
             * @notice     Escrows the ERC721 Token, assigning ownership to this contract. Throws if the escrow fails.
             * @param      _owner    The owner
             * @param      _tokenId  The token identifier
             */
            function _escrow(address _owner, uint256 _tokenId) internal {
                nonFungibleContract.safeTransferFrom(_owner, this, _tokenId);
            }
        
            /**
             * @dev        Internal Transfer function
             * @notice     Transfers an ERC721 Token owned by this contract to another address. Returns true if the transfer succeeds.
             * @param      _owner     The owner
             * @param      _receiver  The receiver
             * @param      _tokenId   The token identifier
             */
            function _transfer(address _owner, address _receiver, uint256 _tokenId) internal {
                nonFungibleContract.transferFrom(_owner, _receiver, _tokenId);
            }
        
            /**
             * @dev        Internal Function to add Sale, which duration check (atleast 1 min duration required)
             * @notice     Adds an sale to the list of open sales. Also fires the SaleCreated event.
             * @param      _tokenId  The token identifier
             * @param      _sale     The sale
             */
            function _addSale(uint256 _tokenId, Sale _sale) internal {
                // Require that all sales have a duration of
                // at least one minute.
                require(_sale.duration >= 1 minutes);
                
                tokenIdToSale[_tokenId] = _sale;
        
                emit SaleCreated(
                    uint256(_tokenId),
                    uint256(_sale.startingPrice),
                    uint256(_sale.endingPrice),
                    uint256(_sale.duration),
                    uint256(_sale.startedAt)
                );
            }
        
            /**
             * @dev        Internal Function to add Team Sale, which duration check (atleast 1 min duration required)
             * @notice     Adds an sale to the list of open sales. Also fires the SaleCreated event.
             * @param      _tokenIds  The token identifiers
             * @param      _sale      The sale
             */
            function _addTeamSale(uint256[9] _tokenIds, Sale _sale) internal {
                // Require that all sales have a duration of
                // at least one minute.
                require(_sale.duration >= 1 minutes);
                
                for(uint ii = 0; ii < 9; ii++) {
                    require(_tokenIds[ii] != 0);
                    require(nonFungibleContract.exists(_tokenIds[ii]));
        
                    tokenIdToSale[_tokenIds[ii]] = _sale;
                }
        
                emit TeamSaleCreated(
                    _tokenIds,
                    uint256(_sale.startingPrice),
                    uint256(_sale.endingPrice),
                    uint256(_sale.duration),
                    uint256(_sale.startedAt)
                );
            }
        
            /**
             * @dev        Facilites Sale cancellation. Also removed the Sale from the array
             * @notice     Cancels an sale (given the collectibleID is not 0). SaleCancelled Event
             * @param      _tokenId  The token identifier
             * @param      _seller   The seller
             */
            function _cancelSale(uint256 _tokenId, address _seller) internal {
                Sale memory saleItem = tokenIdToSale[_tokenId];
        
                //Check for team sale
                if(saleItem.tokenIds[1] != 0) {
                    for(uint ii = 0; ii < 9; ii++) {
                        _removeSale(saleItem.tokenIds[ii]);
                        _transfer(address(this), _seller, saleItem.tokenIds[ii]);
                    }
                    emit SaleCancelled(_tokenId, _seller);
                } else {
                    _removeSale(_tokenId);
                    _transfer(address(this), _seller, _tokenId);
                    emit SaleCancelled(_tokenId, _seller);
                }
            }
        
            /**
             * @dev        Computes the price and transfers winnings. Does NOT transfer ownership of token.
             * @notice     Internal function, helps in making the bid and transferring asset if successful
             * @param      _tokenId    The token identifier
             * @param      _bidAmount  The bid amount
             */
            function _bid(uint256 _tokenId, uint256 _bidAmount)
                internal
                returns (uint256)
            {
                // Get a reference to the sale struct
                Sale storage _sale = tokenIdToSale[_tokenId];
                uint256[9] memory tokenIdsStore = tokenIdToSale[_tokenId].tokenIds;
                
                // Explicitly check that this sale is currently live.
                require(_isOnSale(_sale));
        
                // Check that the bid is greater than or equal to the current price
                uint256 price = _currentPrice(_sale);
                require(_bidAmount >= price);
        
                // Grab a reference to the seller before the sale struct
                // gets deleted.
                address seller = _sale.seller;
        
                // The bid is good! Remove the sale before sending the fees
                // to the sender so we can't have a reentrancy attack.
                if(tokenIdsStore[1] > 0) {
                    for(uint ii = 0; ii < 9; ii++) {
                        _removeSale(tokenIdsStore[ii]);
                    }
                } else {
                    _removeSale(_tokenId);
                }
        
                // Transfer proceeds to seller (if there are any!)
                if (price > 0) {
                    // Calculate the marketplace's cut.
                    // (NOTE: _computeCut() is guaranteed to return a
                    // value <= price)
                    uint256 marketsCut = _computeCut(price);
                    uint256 sellerProceeds = price.sub(marketsCut);
        
                    seller.transfer(sellerProceeds);
                }
        
                // Calculate any excess funds included with the bid. If the excess
                // is anything worth worrying about, transfer it back to bidder.
                uint256 bidExcess = _bidAmount.sub(price);
        
                // Return the funds. Similar to the previous transfer.
                msg.sender.transfer(bidExcess);
        
                // Tell the world!
                // uint256 assetID, uint256 totalPrice, address winner, uint16 generation
                if(tokenIdsStore[1] > 0) {
                    emit TeamSaleWinner(tokenIdsStore, price, msg.sender);
                } else {
                    emit SaleWinner(_tokenId, price, msg.sender);
                }
                
                return price;
            }
        
            /**
             * @dev        Removes an sale from the list of open sales.
             * @notice     Internal Function to remove sales
             * @param      _tokenId  The token identifier
             */
            function _removeSale(uint256 _tokenId) internal {
                delete tokenIdToSale[_tokenId];
            }
        
            /**
             * @dev        Returns true if the FT (ERC721) is on sale.
             * @notice     Internal function to check if an
             * @param      _sale  The sale
             */
            function _isOnSale(Sale memory _sale) internal pure returns (bool) {
                return (_sale.startedAt > 0);
            }
        
            /** @dev Returns current price of an FT (ERC721) on sale. Broken into two
             *  functions (this one, that computes the duration from the sale
             *  structure, and the other that does the price computation) so we
             *  can easily test that the price computation works correctly.
             */
            function _currentPrice(Sale memory _sale)
                internal
                view
                returns (uint256)
            {
                uint256 secondsPassed = 0;
        
                // A bit of insurance against negative values (or wraparound).
                // Probably not necessary (since Ethereum guarnatees that the
                // now variable doesn't ever go backwards).
                if (now > _sale.startedAt.add(BID_DELAY_TIME)) {
                    secondsPassed = now.sub(_sale.startedAt.add(BID_DELAY_TIME));
                }
        
                return _computeCurrentPrice(
                    _sale.startingPrice,
                    _sale.endingPrice,
                    _sale.duration,
                    secondsPassed
                );
            }
        
            /** @dev Computes the current price of an sale. Factored out
             *  from _currentPrice so we can run extensive unit tests.
             *  When testing, make this function public and turn on
             *  `Current price computation` test suite.
             */
            function _computeCurrentPrice(
                uint256 _startingPrice,
                uint256 _endingPrice,
                uint256 _duration,
                uint256 _secondsPassed
            )
                internal
                pure
                returns (uint256)
            {
                // NOTE: We don't use SafeMath (or similar) in this function because
                //  all of our public functions carefully cap the maximum values for
                //  time (at 64-bits) and currency (at 128-bits). _duration is
                //  also known to be non-zero (see the require() statement in
                //  _addSale())
                if (_secondsPassed >= _duration) {
                    // We've reached the end of the dynamic pricing portion
                    // of the sale, just return the end price.
                    return _endingPrice;
                } else {
                    // Starting price can be higher than ending price (and often is!), so
                    // this delta can be negative.
                    int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
        
                    // This multiplication can't overflow, _secondsPassed will easily fit within
                    // 64-bits, and totalPriceChange will easily fit within 128-bits, their product
                    // will always fit within 256-bits.
                    int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration);
        
                    // currentPriceChange can be negative, but if so, will have a magnitude
                    // less that _startingPrice. Thus, this result will always end up positive.
                    int256 currentPrice = int256(_startingPrice) + currentPriceChange;
        
                    return uint256(currentPrice);
                }
            }
        
            /**
             * @dev        Computes owner's cut of a sale.
             * @param      _price  The price
             */
            function _computeCut(uint256 _price) internal view returns (uint256) {
                return _price.mul(ownerCut).div(10000);
            }
        }
        
        /* Clock sales functions and interfaces
         * @title SaleManager
         * @author Fazri Zubair & Farhan Khwaja (Lucid Sight, Inc.)
         */
        contract SaleManager is SaleBase {
        
            /// @dev MAPINGS
            mapping (uint256 => uint256[3]) public lastTeamSalePrices;
            mapping (uint256 => uint256) public lastSingleSalePrices;
            mapping (uint256 => uint256) public seedTeamSaleCount;
            uint256 public seedSingleSaleCount = 0;
        
            /// @dev CONSTANTS
            uint256 public constant SINGLE_SALE_MULTIPLIER = 35;
            uint256 public constant TEAM_SALE_MULTIPLIER = 12;
            uint256 public constant STARTING_PRICE = 10 finney;
            uint256 public constant SALES_DURATION = 1 days;
        
            bool public isBatchSupported = true;
        
            /**
             * @dev        Constructor creates a reference to the MLB_NFT Sale Manager contract
             */
            constructor() public {
                require(ownerCut <= 10000); // You can't collect more than 100% silly ;)
                require(msg.sender != address(0));
                paused = true;
                gameManagerPrimary = msg.sender;
                gameManagerSecondary = msg.sender;
                bankManager = msg.sender;
            }
        
            /// @dev Override unpause so it requires all external contract addresses
            ///  to be set before contract can be unpaused. Also, we can't have
            ///  newContractAddress set either, because then the contract was upgraded.
            /// @notice This is public rather than external so we can call super.unpause
            ///  without using an expensive CALL.
            function unpause() public onlyGameManager whenPaused {
                require(nonFungibleContract != address(0));
        
                // Actually unpause the contract.
                super.unpause();
            }
        
            /** @dev Remove all Ether from the contract, which is the owner's cuts
             *  as well as any Ether sent directly to the contract address.
             *  Always transfers to the NFT (ERC721) contract, but can be called either by
             *  the owner or the NFT (ERC721) contract.
             */
            function _withdrawBalance() internal {
                // We are using this boolean method to make sure that even if one fails it will still work
                bankManager.transfer(address(this).balance);
            }
        
        
            /** @dev Reject all Ether from being sent here, unless it's from one of the
             *  contracts. (Hopefully, we can prevent user accidents.)
             *  @notice No tipping!
             */
            function() external payable {
                address nftAddress = address(nonFungibleContract);
                require(
                    msg.sender == address(this) || 
                    msg.sender == gameManagerPrimary ||
                    msg.sender == gameManagerSecondary ||
                    msg.sender == bankManager ||
                    msg.sender == nftAddress ||
                    msg.sender == address(LSEscrowContract)
                );
            }
        
            /**
             * @dev        Creates and begins a new sale.
             * @param      _tokenId        The token identifier
             * @param      _startingPrice  The starting price
             * @param      _endingPrice    The ending price
             * @param      _duration       The duration
             * @param      _seller         The seller
             */
            function _createSale(
                uint256 _tokenId,
                uint256 _startingPrice,
                uint256 _endingPrice,
                uint256 _duration,
                address _seller
            )
                internal
            {
                Sale memory sale = Sale(
                    _seller,
                    _startingPrice,
                    _endingPrice,
                    _duration,
                    now,
                    [_tokenId,0,0,0,0,0,0,0,0]
                );
                _addSale(_tokenId, sale);
            }
        
            /**
             * @dev        Internal Function, helps in creating team sale
             * @param      _tokenIds       The token identifiers
             * @param      _startingPrice  The starting price
             * @param      _endingPrice    The ending price
             * @param      _duration       The duration
             * @param      _seller         The seller
             */
            function _createTeamSale(
                uint256[9] _tokenIds,
                uint256 _startingPrice,
                uint256 _endingPrice,
                uint256 _duration,
                address _seller)
                internal {
        
                Sale memory sale = Sale(
                    _seller,
                    _startingPrice,
                    _endingPrice,
                    _duration,
                    now,
                    _tokenIds
                );
        
                /// Add sale obj to all tokens
                _addTeamSale(_tokenIds, sale);
            }
        
            /** @dev            Cancels an sale that hasn't been won yet. Returns the MLBNFT (ERC721) to original owner.
             *  @notice         This is a state-modifying function that can be called while the contract is paused.
             */
            function cancelSale(uint256 _tokenId) external whenNotPaused {
                Sale memory sale = tokenIdToSale[_tokenId];
                require(_isOnSale(sale));
                address seller = sale.seller;
                require(msg.sender == seller);
                _cancelSale(_tokenId, seller);
            }
        
            /** @dev        Cancels an sale that hasn't been won yet. Returns the MLBNFT (ERC721) to original owner.
             *  @notice     This is a state-modifying function that can be called while the contract is paused. Can be only called by the GameManagers
             */
            function cancelSaleWhenPaused(uint256 _tokenId) external whenPaused onlyGameManager {
                Sale memory sale = tokenIdToSale[_tokenId];
                require(_isOnSale(sale));
                address seller = sale.seller;
                _cancelSale(_tokenId, seller);
            }
        
            /** 
             * @dev    Returns sales info for an CSLCollectibles (ERC721) on sale.
             * @notice Fetches the details related to the Sale
             * @param  _tokenId    ID of the token on sale
             */
            function getSale(uint256 _tokenId) external view returns (address seller, uint256 startingPrice, uint256 endingPrice, uint256 duration, uint256 startedAt, uint256[9] tokenIds) {
                Sale memory sale = tokenIdToSale[_tokenId];
                require(_isOnSale(sale));
                return (
                    sale.seller,
                    sale.startingPrice,
                    sale.endingPrice,
                    sale.duration,
                    sale.startedAt,
                    sale.tokenIds
                );
            }
        
            /**
             * @dev        Returns the current price of an sale.
             * @param      _tokenId  The token identifier
             */
            function getCurrentPrice(uint256 _tokenId) external view returns (uint256) {
                Sale memory sale = tokenIdToSale[_tokenId];
                require(_isOnSale(sale));
                return _currentPrice(sale);
            }
        
            /** @dev Calculates the new price for Sale Item
             * @param   _saleType     Sale Type Identifier (0 - Single Sale, 1 - Team Sale)
             * @param   _teamId       Team Identifier
             */
            function _averageSalePrice(uint256 _saleType, uint256 _teamId) internal view returns (uint256) {
                uint256 _price = 0;
                if(_saleType == 0) {
                    for(uint256 ii = 0; ii < 10; ii++) {
                        _price = _price.add(lastSingleSalePrices[ii]);
                    }
                    _price = _price.mul(SINGLE_SALE_MULTIPLIER).div(100);
                } else {
                    for (uint256 i = 0; i < 3; i++) {
                        _price = _price.add(lastTeamSalePrices[_teamId][i]);
                    }
                
                    _price = _price.mul(TEAM_SALE_MULTIPLIER).div(30);
                    _price = _price.mul(9);
                }
        
                return _price;
            }
            
            /**
             * @dev        Put a Collectible up for sale. Does some ownership trickery to create sale in one tx.
             * @param      _tokenId        The token identifier
             * @param      _startingPrice  The starting price
             * @param      _endingPrice    The ending price
             * @param      _duration       The duration
             * @param      _owner          Owner of the token
             */
            function createSale(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, address _owner) external whenNotPaused {
                require(msg.sender == address(nonFungibleContract));
        
                // Check whether the collectible is inPlay. If inPlay cant put it on Sale
                require(nonFungibleContract.checkIsAttached(_tokenId) == 0);
                
                _escrow(_owner, _tokenId);
        
                // Sale throws if inputs are invalid and clears
                // transfer and sire approval after escrowing the CSLCollectible.
                _createSale(
                    _tokenId,
                    _startingPrice,
                    _endingPrice,
                    _duration,
                    _owner
                );
            }
        
            /**
             * @dev        Put a Collectible up for sale. Only callable, if user approved contract for 1/All Collectibles
             * @param      _tokenId        The token identifier
             * @param      _startingPrice  The starting price
             * @param      _endingPrice    The ending price
             * @param      _duration       The duration
             */
            function userCreateSaleIfApproved (uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration) external whenNotPaused {
                
                require(nonFungibleContract.getApproved(_tokenId) == address(this) || nonFungibleContract.isApprovedForAll(msg.sender, address(this)));
                
                // Check whether the collectible is inPlay. If inPlay cant put it on Sale
                require(nonFungibleContract.checkIsAttached(_tokenId) == 0);
                
                _escrow(msg.sender, _tokenId);
        
                // Sale throws if inputs are invalid and clears
                // transfer and sire approval after escrowing the CSLCollectible.
                _createSale(
                    _tokenId,
                    _startingPrice,
                    _endingPrice,
                    _duration,
                    msg.sender
                );
            }
        
            /** 
             * @dev        Transfers the balance of the sales manager contract to the CSLCollectible contract. We use two-step withdrawal to
             *              prevent two transfer calls in the sale bid function.
             */
            function withdrawSaleManagerBalances() public onlyBanker {
                _withdrawBalance();
            }
        
            /** 
             *  @dev Function to chnage the OwnerCut only accessible by the Owner of the contract
             *  @param _newCut - Sets the ownerCut to new value
             */
            function setOwnerCut(uint256 _newCut) external onlyBanker {
                require(_newCut <= 10000);
                ownerCut = _newCut;
            }
            
            /**
             * @dev        Facilitates seed collectible auction creation. Enforces strict check on the data being passed
             * @notice     Creates a new Collectible and creates an auction for it.
             * @param      _teamId            The team identifier
             * @param      _posId             The position identifier
             * @param      _attributes        The attributes
             * @param      _playerOverrideId  The player override identifier
             * @param      _mlbPlayerId       The mlb player identifier
             * @param      _startPrice        The start price
             * @param      _endPrice          The end price
             * @param      _saleDuration      The sale duration
             */
            function createSingleSeedAuction(
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId,
                uint256 _startPrice,
                uint256 _endPrice,
                uint256 _saleDuration)
                public
                onlyGameManager
                whenNotPaused {
                // Check to see the NFT address is not 0
                require(nonFungibleContract != address(0));
                require(_teamId != 0);
        
                uint256 nftId = nonFungibleContract.createSeedCollectible(_teamId,_posId,_attributes,address(this),0, _playerOverrideId, _mlbPlayerId);
        
                uint256 startPrice = 0;
                uint256 endPrice = 0;
                uint256 duration = 0;
                
                if(_startPrice == 0) {
                    startPrice = _computeNextSeedPrice(0, _teamId);
                } else {
                    startPrice = _startPrice;
                }
        
                if(_endPrice != 0) {
                    endPrice = _endPrice;
                } else {
                    endPrice = 0;
                }
        
                if(_saleDuration == 0) {
                    duration = SALES_DURATION;
                } else {
                    duration = _saleDuration;
                }
        
                _createSale(
                    nftId,
                    startPrice,
                    endPrice,
                    duration,
                    address(this)
                );
            }
        
            /**
             * @dev        Facilitates promo collectible auction creation. Enforces strict check on the data being passed
             * @notice     Creates a new Collectible and creates an auction for it.
             * @param      _teamId            The team identifier
             * @param      _posId             The position identifier
             * @param      _attributes        The attributes
             * @param      _playerOverrideId  The player override identifier
             * @param      _mlbPlayerId       The mlb player identifier
             * @param      _startPrice        The start price
             * @param      _endPrice          The end price
             * @param      _saleDuration      The sale duration
             */
            function createPromoSeedAuction(
                uint8 _teamId,
                uint8 _posId,
                uint256 _attributes,
                uint256 _playerOverrideId,
                uint256 _mlbPlayerId,
                uint256 _startPrice,
                uint256 _endPrice,
                uint256 _saleDuration)
                public
                onlyGameManager
                whenNotPaused {
                // Check to see the NFT address is not 0
                require(nonFungibleContract != address(0));
                require(_teamId != 0);
        
                uint256 nftId = nonFungibleContract.createPromoCollectible(_teamId, _posId, _attributes, address(this), 0, _playerOverrideId, _mlbPlayerId);
        
                uint256 startPrice = 0;
                uint256 endPrice = 0;
                uint256 duration = 0;
                
                if(_startPrice == 0) {
                    startPrice = _computeNextSeedPrice(0, _teamId);
                } else {
                    startPrice = _startPrice;
                }
        
                if(_endPrice != 0) {
                    endPrice = _endPrice;
                } else {
                    endPrice = 0;
                }
        
                if(_saleDuration == 0) {
                    duration = SALES_DURATION;
                } else {
                    duration = _saleDuration;
                }
        
                _createSale(
                    nftId,
                    startPrice,
                    endPrice,
                    duration,
                    address(this)
                );
            }
        
            /**
             * @dev        Creates Team Sale Auction
             * @param      _teamId        The team identifier
             * @param      _tokenIds      The token identifiers
             * @param      _startPrice    The start price
             * @param      _endPrice      The end price
             * @param      _saleDuration  The sale duration
             */
            function createTeamSaleAuction(
                uint8 _teamId,
                uint256[9] _tokenIds,
                uint256 _startPrice,
                uint256 _endPrice,
                uint256 _saleDuration)
                public
                onlyGameManager
                whenNotPaused {
        
                require(_teamId != 0);
        
                // Helps in not creating sale with wrong team and player combination
                for(uint ii = 0; ii < _tokenIds.length; ii++){
                    require(nonFungibleContract.getTeamId(_tokenIds[ii]) == _teamId);
                }
                
                uint256 startPrice = 0;
                uint256 endPrice = 0;
                uint256 duration = 0;
                
                if(_startPrice == 0) {
                    startPrice = _computeNextSeedPrice(1, _teamId).mul(9);
                } else {
                    startPrice = _startPrice;
                }
        
                if(_endPrice != 0) {
                    endPrice = _endPrice;
                } else {
                    endPrice = 0;
                }
        
                if(_saleDuration == 0) {
                    duration = SALES_DURATION;
                } else {
                    duration = _saleDuration;
                }
        
                _createTeamSale(
                    _tokenIds,
                    startPrice,
                    endPrice,
                    duration,
                    address(this)
                );
            }
        
            /**
             * @dev        Computes the next auction starting price
             * @param      _saleType     The sale type
             * @param      _teamId       The team identifier
             */
            function _computeNextSeedPrice(uint256 _saleType, uint256 _teamId) internal view returns (uint256) {
                uint256 nextPrice = _averageSalePrice(_saleType, _teamId);
        
                // Sanity check to ensure we don't overflow arithmetic
                require(nextPrice == nextPrice);
        
                // We never auction for less than starting price
                if (nextPrice < STARTING_PRICE) {
                    nextPrice = STARTING_PRICE;
                }
        
                return nextPrice;
            }
        
            /**
             * @dev        Sanity check that allows us to ensure that we are pointing to the right sale call.
             */
            bool public isSalesManager = true;
        
            /**
             * @dev        works the same as default bid method.
             * @param      _tokenId  The token identifier
             */
            function bid(uint256 _tokenId) public whenNotPaused payable {
                
                Sale memory sale = tokenIdToSale[_tokenId];
                address seller = sale.seller;
        
                // This check is added to give all users a level playing field to think & bid on the player
                require (now > sale.startedAt.add(BID_DELAY_TIME));
                
                uint256 price = _bid(_tokenId, msg.value);
        
                //If multi token sale
                if(sale.tokenIds[1] > 0) {
                    
                    for (uint256 i = 0; i < 9; i++) {
                        _transfer(address(this), msg.sender, sale.tokenIds[i]);
                    }
        
                    // Avg price
                    price = price.div(9);
                } else {
                    
                    _transfer(address(this), msg.sender, _tokenId);
                }
                
                // If not a seed, exit
                if (seller == address(this)) {
                    if(sale.tokenIds[1] > 0){
                        uint256 _teamId = nonFungibleContract.getTeamId(_tokenId);
        
                        lastTeamSalePrices[_teamId][seedTeamSaleCount[_teamId] % 3] = price;
        
                        seedTeamSaleCount[_teamId]++;
                    } else {
                        lastSingleSalePrices[seedSingleSaleCount % 10] = price;
                        seedSingleSaleCount++;
                    }
                }
            }
            
            /**
             * @dev        Sets the address for the NFT Contract
             * @param      _nftAddress  The nft address
             */
            function setNFTContractAddress(address _nftAddress) public onlyGameManager {
                require (_nftAddress != address(0));        
                nonFungibleContract = MLBNFT(_nftAddress);
            }
        
            /**
             * @dev        Added this module to allow retrieve of accidental asset transfer to contract
             * @param      _to       { parameter_description }
             * @param      _tokenId  The token identifier
             */
            function assetTransfer(address _to, uint256 _tokenId) public onlyGameManager {
                require(_tokenId != 0);
                nonFungibleContract.transferFrom(address(this), _to, _tokenId);
            }
        
             /**
             * @dev        Added this module to allow retrieve of accidental asset transfer to contract
             * @param      _to       { parameter_description }
             * @param      _tokenIds  The token identifiers
             */
            function batchAssetTransfer(address _to, uint256[] _tokenIds) public onlyGameManager {
                require(isBatchSupported);
                require (_tokenIds.length > 0);
                
                for(uint i = 0; i < _tokenIds.length; i++){
                    require(_tokenIds[i] != 0);
                    nonFungibleContract.transferFrom(address(this), _to, _tokenIds[i]);
                }
            }
        
            /**
             * @dev        Creates new Seed Team Collectibles
             * @notice     Creates a team and transfers all minted assets to SaleManager
             * @param      _teamId       The team identifier
             * @param      _attributes   The attributes
             * @param      _mlbPlayerId  The mlb player identifier
             */
            function createSeedTeam(uint8 _teamId, uint256[9] _attributes, uint256[9] _mlbPlayerId) public onlyGameManager whenNotPaused {
                require(_teamId != 0);
                
                for(uint ii = 0; ii < 9; ii++) {
                    nonFungibleContract.createSeedCollectible(_teamId, uint8(ii.add(1)), _attributes[ii], address(this), 0, 0, _mlbPlayerId[ii]);
                }
            }
        
            /**
             * @dev            Cancels an sale that hasn't been won yet. Returns the MLBNFT (ERC721) to original owner.
             * @notice         This is a state-modifying function that can be called while the contract is paused.
             */
            function batchCancelSale(uint256[] _tokenIds) external whenNotPaused {
                require(isBatchSupported);
                require(_tokenIds.length > 0);
        
                for(uint ii = 0; ii < _tokenIds.length; ii++){
                    Sale memory sale = tokenIdToSale[_tokenIds[ii]];
                    require(_isOnSale(sale));
                    
                    address seller = sale.seller;
                    require(msg.sender == seller);
        
                    _cancelSale(_tokenIds[ii], seller);
                }
            }
        
            /**
             * @dev        Helps to toggle batch supported flag
             * @param      _flag  The flag
             */
            function updateBatchSupport(bool _flag) public onlyGameManager {
                isBatchSupported = _flag;
            }
        
            /**
             * @dev        Batching Operation: Creates a new Collectible and creates an auction for it.
             * @notice     Helps in creating single seed auctions in batches
             * @param      _teamIds            The team identifier
             * @param      _posIds            The position identifier
             * @param      _attributes        The attributes
             * @param      _playerOverrideIds  The player override identifier
             * @param      _mlbPlayerIds       The mlb player identifier
             * @param      _startPrice         The start price
             */
            function batchCreateSingleSeedAuction(
                uint8[] _teamIds,
                uint8[] _posIds,
                uint256[] _attributes,
                uint256[] _playerOverrideIds,
                uint256[] _mlbPlayerIds,
                uint256 _startPrice)
                public
                onlyGameManager
                whenNotPaused {
        
                require (isBatchSupported);
        
                require (_teamIds.length > 0 &&
                    _posIds.length > 0 &&
                    _attributes.length > 0 &&
                    _playerOverrideIds.length > 0 &&
                    _mlbPlayerIds.length > 0 );
                
                // Check to see the NFT address is not 0
                require(nonFungibleContract != address(0));
                
                uint256 nftId;
        
                require (_startPrice != 0);
        
                for(uint ii = 0; ii < _mlbPlayerIds.length; ii++){
                    require(_teamIds[ii] != 0);
        
                    nftId = nonFungibleContract.createSeedCollectible(
                                _teamIds[ii],
                                _posIds[ii],
                                _attributes[ii],
                                address(this),
                                0,
                                _playerOverrideIds[ii],
                                _mlbPlayerIds[ii]);
        
                    _createSale(
                        nftId,
                        _startPrice,
                        0,
                        SALES_DURATION,
                        address(this)
                    );
                }
            }
        
            /**
             * @dev        Helps in incrementing the delay time to start bidding for any auctions
             * @notice     Function helps to update the delay time for bidding
             * @param      _newDelay       The new Delay time
             */
            function updateDelayTime(uint256 _newDelay) public onlyGameManager whenNotPaused {
        
                BID_DELAY_TIME = _newDelay;
            }
        
            function bidTransfer(uint256 _tokenId, address _buyer, uint256 _bidAmount) public canTransact {
        
                Sale memory sale = tokenIdToSale[_tokenId];
                address seller = sale.seller;
        
                // This check is added to give all users a level playing field to think & bid on the player
                require (now > sale.startedAt.add(BID_DELAY_TIME));
                
                uint256[9] memory tokenIdsStore = tokenIdToSale[_tokenId].tokenIds;
                
                // Explicitly check that this sale is currently live.
                require(_isOnSale(sale));
        
                // Check that the bid is greater than or equal to the current price
                uint256 price = _currentPrice(sale);
                require(_bidAmount >= price);
        
                // The bid is good! Remove the sale before sending the fees
                // to the sender so we can't have a reentrancy attack.
                if(tokenIdsStore[1] > 0) {
                    for(uint ii = 0; ii < 9; ii++) {
                        _removeSale(tokenIdsStore[ii]);
                    }
                } else {
                    _removeSale(_tokenId);
                }
        
                uint256 marketsCut = 0;
                uint256 sellerProceeds = 0;
        
                // Transfer proceeds to seller (if there are any!)
                if (price > 0) {
                    // Calculate the marketplace's cut.
                    // (NOTE: _computeCut() is guaranteed to return a
                    // value <= price)
                    marketsCut = _computeCut(price);
                    sellerProceeds = price.sub(marketsCut);
                }
        
                //escrowTransfer(address seller, address buyer, uint256 currentPrice) public returns(bool);
                require (LSEscrowContract.escrowTransfer(seller, _buyer, sellerProceeds, marketsCut));
                
                // Tell the world!
                // uint256 assetID, uint256 totalPrice, address winner, uint16 generation
                if(tokenIdsStore[1] > 0) {
                    emit TeamSaleWinner(tokenIdsStore, price, _buyer);
                } else {
                    emit SaleWinner(_tokenId, price, _buyer);
                }
        
                //If multi token sale
                if(sale.tokenIds[1] > 0) {
                    
                    for (uint256 i = 0; i < 9; i++) {
                        _transfer(address(this), _buyer, sale.tokenIds[i]);
                    }
        
                    // Avg price
                    price = price.div(9);
                } else {
                    
                    _transfer(address(this), _buyer, _tokenId);
                }
                
                // If not a seed, exit
                if (seller == address(this)) {
                    if(sale.tokenIds[1] > 0) {
                        uint256 _teamId = nonFungibleContract.getTeamId(_tokenId);
        
                        lastTeamSalePrices[_teamId][seedTeamSaleCount[_teamId] % 3] = price;
        
                        seedTeamSaleCount[_teamId]++;
                    } else {
                        lastSingleSalePrices[seedSingleSaleCount % 10] = price;
                        seedSingleSaleCount++;
                    }
                }
            }
        
            /**
             * @dev        Sets the address for the LS Escrow Contract
             * @param      _lsEscrowAddress  The nft address
             */
            function setLSEscrowContractAddress(address _lsEscrowAddress) public onlyGameManager {
                require (_lsEscrowAddress != address(0));        
                LSEscrowContract = LSEscrow(_lsEscrowAddress);
            }
        }