ETH Price: $2,055.81 (-2.98%)

Transaction Decoder

Block:
13225675 at Sep-14-2021 07:21:31 PM +UTC
Transaction Fee:
0.01018046091403125 ETH $20.93
Gas Used:
140,625 Gas / 72.394388722 Gwei

Emitted Events:

145 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000bc7de2b767fa97ea9d5ebc05a8d0c8557dcabd7b, 0x0000000000000000000000006fefa5233289ad1c5c4babcb8f44952032f20e78, 0x0000000000000000000000000000000000000000000000000000000000062a23 )

Account State Difference:

  Address   Before After State Difference Code
0x2D6e1056...Ed58637C0
(MiningPoolHub)
2,353.768880800367389329 Eth2,353.769091737867389329 Eth0.0002109375
0xbc7de2B7...57dCABd7b
9.217407609578073581 Eth
Nonce: 7235
9.207227148664042331 Eth
Nonce: 7236
0.01018046091403125

Execution Trace

AdminUpgradeabilityProxy.23b872dd( )
  • GunToken.transferFrom( from=0xbc7de2B767fA97Ea9D5Ebc05A8d0C8557dCABd7b, to=0x6FefA5233289ad1c5C4BAbCB8F44952032F20E78, tokenId=404003 )
    File 1 of 2: AdminUpgradeabilityProxy
    // File: @openzeppelin/upgrades/contracts/upgradeability/Proxy.sol
    
    pragma solidity ^0.5.0;
    
    /**
     * @title Proxy
     * @dev Implements delegation of calls to other contracts, with proper
     * forwarding of return values and bubbling of failures.
     * It defines a fallback function that delegates all calls to the address
     * returned by the abstract _implementation() internal function.
     */
    contract Proxy {
      /**
       * @dev Fallback function.
       * Implemented entirely in `_fallback`.
       */
      function () payable external {
        _fallback();
      }
    
      /**
       * @return The Address of the implementation.
       */
      function _implementation() internal view returns (address);
    
      /**
       * @dev Delegates execution to an implementation contract.
       * This is a low level function that doesn't return to its internal call site.
       * It will return to the external caller whatever the implementation returns.
       * @param implementation Address to delegate.
       */
      function _delegate(address implementation) internal {
        assembly {
          // Copy msg.data. We take full control of memory in this inline assembly
          // block because it will not return to Solidity code. We overwrite the
          // Solidity scratch pad at memory position 0.
          calldatacopy(0, 0, calldatasize)
    
          // Call the implementation.
          // out and outsize are 0 because we don't know the size yet.
          let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
    
          // Copy the returned data.
          returndatacopy(0, 0, returndatasize)
    
          switch result
          // delegatecall returns 0 on error.
          case 0 { revert(0, returndatasize) }
          default { return(0, returndatasize) }
        }
      }
    
      /**
       * @dev Function that is run as the first thing in the fallback function.
       * Can be redefined in derived contracts to add functionality.
       * Redefinitions must call super._willFallback().
       */
      function _willFallback() internal {
      }
    
      /**
       * @dev fallback implementation.
       * Extracted to enable manual triggering.
       */
      function _fallback() internal {
        _willFallback();
        _delegate(_implementation());
      }
    }
    
    // File: @openzeppelin/upgrades/contracts/utils/Address.sol
    
    pragma solidity ^0.5.0;
    
    /**
     * Utility library of inline functions on addresses
     *
     * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
     * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
     * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
     * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
     */
    library OpenZeppelinUpgradesAddress {
        /**
         * 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 account address of the account to check
         * @return whether the target address is a contract
         */
        function isContract(address account) 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.
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
    }
    
    // File: @openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol
    
    pragma solidity ^0.5.0;
    
    
    
    /**
     * @title BaseUpgradeabilityProxy
     * @dev This contract implements a proxy that allows to change the
     * implementation address to which it will delegate.
     * Such a change is called an implementation upgrade.
     */
    contract BaseUpgradeabilityProxy is Proxy {
      /**
       * @dev Emitted when the implementation is upgraded.
       * @param implementation Address of the new implementation.
       */
      event Upgraded(address indexed implementation);
    
      /**
       * @dev Storage slot with the address of the current implementation.
       * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
       * validated in the constructor.
       */
      bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
      /**
       * @dev Returns the current implementation.
       * @return Address of the current implementation
       */
      function _implementation() internal view returns (address impl) {
        bytes32 slot = IMPLEMENTATION_SLOT;
        assembly {
          impl := sload(slot)
        }
      }
    
      /**
       * @dev Upgrades the proxy to a new implementation.
       * @param newImplementation Address of the new implementation.
       */
      function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
      }
    
      /**
       * @dev Sets the implementation address of the proxy.
       * @param newImplementation Address of the new implementation.
       */
      function _setImplementation(address newImplementation) internal {
        require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
    
        bytes32 slot = IMPLEMENTATION_SLOT;
    
        assembly {
          sstore(slot, newImplementation)
        }
      }
    }
    
    // File: @openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol
    
    pragma solidity ^0.5.0;
    
    
    /**
     * @title UpgradeabilityProxy
     * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
     * implementation and init data.
     */
    contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
      /**
       * @dev Contract constructor.
       * @param _logic Address of the initial implementation.
       * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
       * It should include the signature and the parameters of the function to be called, as described in
       * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
       * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
       */
      constructor(address _logic, bytes memory _data) public payable {
        assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
        _setImplementation(_logic);
        if(_data.length > 0) {
          (bool success,) = _logic.delegatecall(_data);
          require(success);
        }
      }  
    }
    
    // File: @openzeppelin/upgrades/contracts/upgradeability/BaseAdminUpgradeabilityProxy.sol
    
    pragma solidity ^0.5.0;
    
    
    /**
     * @title BaseAdminUpgradeabilityProxy
     * @dev This contract combines an upgradeability proxy with an authorization
     * mechanism for administrative tasks.
     * All external functions in this contract must be guarded by the
     * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
     * feature proposal that would enable this to be done automatically.
     */
    contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
      /**
       * @dev Emitted when the administration has been transferred.
       * @param previousAdmin Address of the previous admin.
       * @param newAdmin Address of the new admin.
       */
      event AdminChanged(address previousAdmin, address newAdmin);
    
      /**
       * @dev Storage slot with the admin of the contract.
       * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
       * validated in the constructor.
       */
    
      bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
    
      /**
       * @dev Modifier to check whether the `msg.sender` is the admin.
       * If it is, it will run the function. Otherwise, it will delegate the call
       * to the implementation.
       */
      modifier ifAdmin() {
        if (msg.sender == _admin()) {
          _;
        } else {
          _fallback();
        }
      }
    
      /**
       * @return The address of the proxy admin.
       */
      function admin() external ifAdmin returns (address) {
        return _admin();
      }
    
      /**
       * @return The address of the implementation.
       */
      function implementation() external ifAdmin returns (address) {
        return _implementation();
      }
    
      /**
       * @dev Changes the admin of the proxy.
       * Only the current admin can call this function.
       * @param newAdmin Address to transfer proxy administration to.
       */
      function changeAdmin(address newAdmin) external ifAdmin {
        require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
        emit AdminChanged(_admin(), newAdmin);
        _setAdmin(newAdmin);
      }
    
      /**
       * @dev Upgrade the backing implementation of the proxy.
       * Only the admin can call this function.
       * @param newImplementation Address of the new implementation.
       */
      function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeTo(newImplementation);
      }
    
      /**
       * @dev Upgrade the backing implementation of the proxy and call a function
       * on the new implementation.
       * This is useful to initialize the proxied contract.
       * @param newImplementation Address of the new implementation.
       * @param data Data to send as msg.data in the low level call.
       * It should include the signature and the parameters of the function to be called, as described in
       * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
       */
      function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
        _upgradeTo(newImplementation);
        (bool success,) = newImplementation.delegatecall(data);
        require(success);
      }
    
      /**
       * @return The admin slot.
       */
      function _admin() internal view returns (address adm) {
        bytes32 slot = ADMIN_SLOT;
        assembly {
          adm := sload(slot)
        }
      }
    
      /**
       * @dev Sets the address of the proxy admin.
       * @param newAdmin Address of the new proxy admin.
       */
      function _setAdmin(address newAdmin) internal {
        bytes32 slot = ADMIN_SLOT;
    
        assembly {
          sstore(slot, newAdmin)
        }
      }
    
      /**
       * @dev Only fall back when the sender is not the admin.
       */
      function _willFallback() internal {
        require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
        super._willFallback();
      }
    }
    
    // File: @openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol
    
    pragma solidity ^0.5.0;
    
    
    /**
     * @title AdminUpgradeabilityProxy
     * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 
     * initializing the implementation, admin, and init data.
     */
    contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
      /**
       * Contract constructor.
       * @param _logic address of the initial implementation.
       * @param _admin Address of the proxy administrator.
       * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
       * It should include the signature and the parameters of the function to be called, as described in
       * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
       * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
       */
      constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
        assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
        _setAdmin(_admin);
      }
    }

    File 2 of 2: GunToken
    pragma solidity ^0.5.0;
    
    /*
     * @title String & slice utility library for Solidity contracts.
     * @author Nick Johnson <arachnid@notdot.net>
     *
     * @dev Functionality in this library is largely implemented using an
     *      abstraction called a 'slice'. A slice represents a part of a string -
     *      anything from the entire string to a single character, or even no
     *      characters at all (a 0-length slice). Since a slice only has to specify
     *      an offset and a length, copying and manipulating slices is a lot less
     *      expensive than copying and manipulating the strings they reference.
     *
     *      To further reduce gas costs, most functions on slice that need to return
     *      a slice modify the original one instead of allocating a new one; for
     *      instance, `s.split(".")` will return the text up to the first '.',
     *      modifying s to only contain the remainder of the string after the '.'.
     *      In situations where you do not want to modify the original slice, you
     *      can make a copy first with `.copy()`, for example:
     *      `s.copy().split(".")`. Try and avoid using this idiom in loops; since
     *      Solidity has no memory management, it will result in allocating many
     *      short-lived slices that are later discarded.
     *
     *      Functions that return two slices come in two versions: a non-allocating
     *      version that takes the second slice as an argument, modifying it in
     *      place, and an allocating version that allocates and returns the second
     *      slice; see `nextRune` for example.
     *
     *      Functions that have to copy string data will return strings rather than
     *      slices; these can be cast back to slices for further processing if
     *      required.
     *
     *      For convenience, some functions are provided with non-modifying
     *      variants that create a new slice and return both; for instance,
     *      `s.splitNew('.')` leaves s unmodified, and returns two values
     *      corresponding to the left and right parts of the string.
     */
    
    library strings {
        struct slice {
            uint _len;
            uint _ptr;
        }
    
        function memcpy(uint dest, uint src, uint len) private pure {
            // Copy word-length chunks while possible
            for(; len >= 32; len -= 32) {
                assembly {
                    mstore(dest, mload(src))
                }
                dest += 32;
                src += 32;
            }
    
            // Copy remaining bytes
            uint mask = 256 ** (32 - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask))
                let destpart := and(mload(dest), mask)
                mstore(dest, or(destpart, srcpart))
            }
        }
    
        /*
         * @dev Returns a slice containing the entire string.
         * @param self The string to make a slice from.
         * @return A newly allocated slice containing the entire string.
         */
        function toSlice(string memory self) internal pure returns (slice memory) {
            uint ptr;
            assembly {
                ptr := add(self, 0x20)
            }
            return slice(bytes(self).length, ptr);
        }
    
        /*
         * @dev Returns a newly allocated string containing the concatenation of
         *      `self` and `other`.
         * @param self The first slice to concatenate.
         * @param other The second slice to concatenate.
         * @return The concatenation of the two strings.
         */
        function concat(slice memory self, slice memory other) internal pure returns (string memory) {
            string memory ret = new string(self._len + other._len);
            uint retptr;
            assembly { retptr := add(ret, 32) }
            memcpy(retptr, self._ptr, self._len);
            memcpy(retptr + self._len, other._ptr, other._len);
            return ret;
        }
    }
    
    // File: @openzeppelin/upgrades/contracts/Initializable.sol
    
    pragma solidity >=0.4.24 <0.7.0;
    
    
    /**
     * @title Initializable
     *
     * @dev Helper contract to support initializer functions. To use it, replace
     * the constructor with a function that has the `initializer` modifier.
     * WARNING: Unlike constructors, initializer functions must be manually
     * invoked. This applies both to deploying an Initializable contract, as well
     * as extending an Initializable contract via inheritance.
     * WARNING: When used with inheritance, manual care must be taken to not invoke
     * a parent initializer twice, or ensure that all initializers are idempotent,
     * because this is not dealt with automatically as with constructors.
     */
    contract Initializable {
    
      /**
       * @dev Indicates that the contract has been initialized.
       */
      bool private initialized;
    
      /**
       * @dev Indicates that the contract is in the process of being initialized.
       */
      bool private initializing;
    
      /**
       * @dev Modifier to use in the initializer function of a contract.
       */
      modifier initializer() {
        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
    
        bool isTopLevelCall = !initializing;
        if (isTopLevelCall) {
          initializing = true;
          initialized = true;
        }
    
        _;
    
        if (isTopLevelCall) {
          initializing = false;
        }
      }
    
      /// @dev Returns true if and only if the function is running in the constructor
      function isConstructor() private view returns (bool) {
        // extcodesize checks the size of the code stored in an address, and
        // address returns the current address. Since the code is still not
        // deployed when running a constructor, any checks on its code size will
        // yield zero, making it an effective way to detect if a contract is
        // under construction or not.
        address self = address(this);
        uint256 cs;
        assembly { cs := extcodesize(self) }
        return cs == 0;
      }
    
      // Reserved storage space to allow for layout changes in the future.
      uint256[50] private ______gap;
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
    
    pragma solidity ^0.5.0;
    
    
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with GSN meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    contract Context is Initializable {
        // Empty internal constructor, to prevent people from mistakenly deploying
        // an instance of this contract, which should be used via inheritance.
        constructor () internal { }
        // solhint-disable-previous-line no-empty-blocks
    
        function _msgSender() internal view returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/introspection/IERC165.sol
    
    pragma solidity ^0.5.0;
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/introspection/ERC165.sol
    
    pragma solidity ^0.5.0;
    
    
    
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts may inherit from this and call {_registerInterface} to declare
     * their support of an interface.
     */
    contract ERC165 is Initializable, IERC165 {
        /*
         * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
         */
        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    
        /**
         * @dev Mapping of interface ids to whether or not it's supported.
         */
        mapping(bytes4 => bool) private _supportedInterfaces;
    
        function initialize() public initializer {
            // Derived contracts need only register support for their own interfaces,
            // we register support for ERC165 itself here
            _registerInterface(_INTERFACE_ID_ERC165);
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         *
         * Time complexity O(1), guaranteed to always use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) public view returns (bool) {
            return _supportedInterfaces[interfaceId];
        }
    
        /**
         * @dev Registers the contract as an implementer of the interface defined by
         * `interfaceId`. Support of the actual ERC165 interface is automatic and
         * registering its interface id is not required.
         *
         * See {IERC165-supportsInterface}.
         *
         * Requirements:
         *
         * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
         */
        function _registerInterface(bytes4 interfaceId) internal {
            require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
            _supportedInterfaces[interfaceId] = true;
        }
    
        uint256[50] private ______gap;
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721.sol
    
    pragma solidity ^0.5.0;
    
    
    
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    contract IERC721 is Initializable, IERC165 {
        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);
    
        /**
         * @dev Returns the number of NFTs in `owner`'s account.
         */
        function balanceOf(address owner) public view returns (uint256 balance);
    
        /**
         * @dev Returns the owner of the NFT specified by `tokenId`.
         */
        function ownerOf(uint256 tokenId) public view returns (address owner);
    
        /**
         * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
         * another (`to`).
         *
         *
         *
         * Requirements:
         * - `from`, `to` cannot be zero.
         * - `tokenId` must be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this
         * NFT by either {approve} or {setApprovalForAll}.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId) public;
        /**
         * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
         * another (`to`).
         *
         * Requirements:
         * - If the caller is not `from`, it must be approved to move this NFT by
         * either {approve} or {setApprovalForAll}.
         */
        function transferFrom(address from, address to, uint256 tokenId) public;
        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 safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol
    
    pragma solidity ^0.5.5;
    
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * This test is non-exhaustive, and there may be false-negatives: during the
         * execution of a contract's constructor, its address will be reported as
         * not containing a contract.
         *
         * IMPORTANT: It is unsafe to assume that an address for which this
         * function returns false is an externally-owned account (EOA) and not a
         * contract.
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies in extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
    
            // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
            // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
            // for accounts without code, i.e. `keccak256('')`
            bytes32 codehash;
            bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
            // solhint-disable-next-line no-inline-assembly
            assembly { codehash := extcodehash(account) }
            return (codehash != 0x0 && codehash != accountHash);
        }
    
        /**
         * @dev Converts an `address` into `address payable`. Note that this is
         * simply a type cast: the actual underlying value is not changed.
         *
         * _Available since v2.4.0._
         */
        function toPayable(address account) internal pure returns (address payable) {
            return address(uint160(account));
        }
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         *
         * _Available since v2.4.0._
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            // solhint-disable-next-line avoid-call-value
            (bool success, ) = recipient.call.value(amount)("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    }
    
    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721Receiver.sol
    
    pragma solidity ^0.5.0;
    
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    contract IERC721Receiver {
        /**
         * @notice Handle the receipt of an NFT
         * @dev The ERC721 smart contract calls this function on the recipient
         * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
         * otherwise the caller will revert the transaction. The selector to be
         * returned can be obtained as `this.onERC721Received.selector`. This
         * function MAY throw to revert and reject the transfer.
         * Note: the ERC721 contract address is always the message sender.
         * @param operator The address which called `safeTransferFrom` function
         * @param from The address which previously owned the token
         * @param tokenId The NFT identifier which is being transferred
         * @param data Additional data with no specified format
         * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
         */
        function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
        public returns (bytes4);
    }
    
    // File: contracts/GunToken.sol
    
    pragma solidity ^0.5.0;
    
    
    
    
    
    
    
    
    /**
     * ERC-721 implementation that allows
     * tokens (of the same category) to be minted in batches. Each batch
     * contains enough data to generate all
     * token ids inside the batch, and to
     * generate the tokenURI in the batch
     */
    contract GunToken is Initializable, Context, ERC165, IERC721 {
        using strings for *;
        using Address for address;
    
        // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
        // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
        bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
    
         /*
         *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
         *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
         *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
         *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
         *
         *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
         *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
         */
        bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    
        /*
         *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
         *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
         *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
         *
         *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
         */
        bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
    
        /*
         *     bytes4(keccak256('name()')) == 0x06fdde03
         *     bytes4(keccak256('symbol()')) == 0x95d89b41
         *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
         *
         *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
         */
        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
    
        // Mapping from token ID to owner
        mapping (uint256 => address) private _tokenOwner;
    
        // Mapping from token ID to approved address
        mapping (uint256 => address) private _tokenApprovals;
    
        // Mapping from owner to number of owned token
        mapping (address => uint256) private _ownedTokensCount;
    
        // Mapping from owner to operator approvals
        mapping (address => mapping (address => bool)) private _operatorApprovals;
    
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        // Token name
        string private constant _name = "War Riders Gun";
    
        // Token symbol
        string private constant _symbol = "WRG";
    
        address internal factory;
        address internal oldToken;
        uint256 internal migrateCursor;
    
        uint16 public constant maxAllocation = 4000;
        uint256 public lastAllocation;
    
        event BatchTransfer(address indexed from, address indexed to, uint256 indexed batchIndex);
    
        struct Batch {
            address owner;
            uint16 size;
            uint8 category;
            uint256 startId;
            uint256 startTokenId;
        }
    
        Batch[] public allBatches;
        mapping(uint256 => bool) public outOfBatch;
    
        //Used for enumeration
        mapping(address => Batch[]) public batchesOwned;
        //Batch index to owner batch index
        mapping(uint256 => uint256) public ownedBatchIndex;
    
        mapping(uint8 => uint256) internal totalGunsMintedByCategory;
        uint256 internal _totalSupply;
    
        // Mapping from owner to list of owned token IDs
        mapping(address => uint256[]) private _ownedTokens;
    
        // Mapping from token ID to index of the owner tokens list
        mapping(uint256 => uint256) private _ownedTokensIndex;
    
        modifier onlyFactory {
            require(msg.sender == factory, "Not authorized");
            _;
        }
    
        function initialize(address factoryAddress, address oldGunToken) public initializer {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
    
            factory = factoryAddress;
            oldToken = oldGunToken;
    
            _registerInterface(_INTERFACE_ID_ERC721);
            _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
            _registerInterface(_INTERFACE_ID_ERC721_METADATA);
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Returns true if the caller is the current owner.
         */
        function isOwner() public view returns (bool) {
            return _msgSender() == _owner;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * > Note: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public onlyOwner {
           require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    
        function categoryTypeToId(uint8 category, uint256 categoryId) public view returns (uint256) {
            for (uint i = 0; i < allBatches.length; i++) {
                Batch memory a = allBatches[i];
                if (a.category != category)
                    continue;
    
                uint256 endId = a.startId + a.size;
                if (categoryId >= a.startId && categoryId < endId) {
                    uint256 dif = categoryId - a.startId;
    
                    return a.startTokenId + dif;
                }
            }
    
            revert("Category not found");
        }
    
        /**
         * @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) {
            return tokenOfOwner(__owner)[index];
        }
    
        function getBatchCount(address __owner) public view returns(uint256) {
            return batchesOwned[__owner].length;
        }
    
        function getTokensInBatch(address __owner, uint256 index) public view returns (uint256[] memory) {
            Batch memory a = batchesOwned[__owner][index];
            uint256[] memory result = new uint256[](a.size);
    
            uint256 pos = 0;
            uint end = a.startTokenId + a.size;
            for (uint i = a.startTokenId; i < end; i++) {
                if (outOfBatch[i]) {
                    continue;
                }
    
                result[pos] = i;
                pos++;
            }
    
            if (pos == 0) {
              return new uint256[](0);
            }
    
            uint256 subAmount = a.size - pos;
    
            assembly { mstore(result, sub(mload(result), subAmount)) }
    
            return result;
        }
    
        function tokenByIndex(uint256 index) public view returns (uint256) {
            return allTokens()[index];
        }
    
        function allTokens() public view returns (uint256[] memory) {
            uint256[] memory result = new uint256[](totalSupply());
    
            uint pos = 0;
            for (uint i = 0; i < allBatches.length; i++) {
                Batch memory a = allBatches[i];
                uint end = a.startTokenId + a.size;
                for (uint j = a.startTokenId; j < end; j++) {
                    result[pos] = j;
                    pos++;
                }
            }
    
            return result;
        }
    
        function tokenOfOwner(address __owner) public view returns (uint256[] memory) {
            uint256[] memory result = new uint256[](balanceOf(__owner));
    
            uint pos = 0;
            for (uint i = 0; i < batchesOwned[__owner].length; i++) {
                Batch memory a = batchesOwned[__owner][i];
                uint end = a.startTokenId + a.size;
                for (uint j = a.startTokenId; j < end; j++) {
                    if (outOfBatch[j]) {
                        continue;
                    }
    
                    result[pos] = j;
                    pos++;
                }
            }
    
            uint256[] memory fallbackOwned = _tokensOfOwner(__owner);
            for (uint i = 0; i < fallbackOwned.length; i++) {
                result[pos] = fallbackOwned[i];
                pos++;
            }
    
            return result;
        }
    
        function exists(uint256 _tokenId) public view returns (bool) {
            if (outOfBatch[_tokenId]) {
                address __owner = _tokenOwner[_tokenId];
                return __owner != address(0);
            } else {
                uint256 index = getBatchIndex(_tokenId);
                if (index < allBatches.length) {
                    Batch memory a = allBatches[index];
                    uint end = a.startTokenId + a.size;
    
                    return _tokenId < end;
                }
                return false;
            }
        }
    
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
    
        function claimAllocation(address to, uint16 size, uint8 category) public onlyFactory returns (uint) {
            require(size < maxAllocation, "Size must be smaller than maxAllocation");
    
            allBatches.push(Batch({
                owner: to,
                size: size,
                category: category,
                startId: totalGunsMintedByCategory[category],
                startTokenId: lastAllocation
            }));
    
            uint end = lastAllocation + size;
            for (uint i = lastAllocation; i < end; i++) {
                emit Transfer(address(0), to, i);
            }
    
            lastAllocation += maxAllocation;
    
            _ownedTokensCount[to] += size;
            totalGunsMintedByCategory[category] += size;
    
            _addBatchToOwner(to, allBatches[allBatches.length - 1]);
    
            _totalSupply += size;
            return lastAllocation;
        }
    
        function migrate(uint256 count) public onlyOwner returns (uint256) {
            GunToken oldGuns = GunToken(oldToken);
    
            for (uint256 i = 0; i < count; i++) {
                uint256 index = migrateCursor + i;
    
                (address to, uint16 size, uint8 category, uint256 startId, uint256 startTokenId) = oldGuns.allBatches(index);
                allBatches.push(Batch({
                    owner: to,
                    size: size,
                    category: category,
                    startId: startId,
                    startTokenId: startTokenId
                }));
    
                uint end = lastAllocation + size;
                uint256 ubalance = size;
                for (uint z = lastAllocation; z < end; z++) {
                    address __owner = oldGuns.ownerOf(z);
                    if (__owner != to) {
                        outOfBatch[z] = true;
                        _addTokenTo(__owner, z);
                        ubalance--;
                        emit Transfer(address(0), __owner, z);
                    } else {
                        emit Transfer(address(0), to, z);
                    }
                }
    
                lastAllocation += maxAllocation;
                _ownedTokensCount[to] += ubalance;
                totalGunsMintedByCategory[category] += size;
    
                _addBatchToOwner(to, allBatches[allBatches.length - 1]);
    
                _totalSupply += size;
            }
    
            migrateCursor += count;
    
            return lastAllocation;
        }
    
        function getBatchIndex(uint256 tokenId) public pure returns (uint256) {
            uint256 index = (tokenId / maxAllocation);
    
            return index;
        }
    
        function categoryForToken(uint256 tokenId) public view returns (uint8) {
            uint256 index = getBatchIndex(tokenId);
            require(index < allBatches.length, "Token batch doesn't exist");
    
            Batch memory a = allBatches[index];
    
            return a.category;
        }
    
        function categoryIdForToken(uint256 tokenId) public view returns (uint256) {
            uint256 index = getBatchIndex(tokenId);
            require(index < allBatches.length, "Token batch doesn't exist");
    
            Batch memory a = allBatches[index];
    
            uint256 categoryId = (tokenId % maxAllocation) + a.startId;
    
            return categoryId;
        }
    
        function uintToString(uint v) internal pure returns (string memory) {
            if (v == 0) {
                return "0";
            }
            uint j = v;
            uint len;
            while (j != 0) {
                len++;
                j /= 10;
            }
            bytes memory bstr = new bytes(len);
            uint k = len - 1;
            while (v != 0) {
                bstr[k--] = byte(uint8(48 + v % 10));
                v /= 10;
            }
    
            return string(bstr);
        }
    
        function tokenURI(uint256 tokenId) public view returns (string memory) {
            require(exists(tokenId), "Token doesn't exist!");
            //Predict the token URI
            uint8 category = categoryForToken(tokenId);
            uint256 _categoryId = categoryIdForToken(tokenId);
    
            string memory id = uintToString(category).toSlice().concat("/".toSlice()).toSlice().concat(uintToString(_categoryId).toSlice().concat(".json".toSlice()).toSlice());
            string memory _base = "https://vault.warriders.com/guns/";
    
            //Final URL: https://vault.warriders.com/guns/<category>/<category_id>.json
            string memory _metadata = _base.toSlice().concat(id.toSlice());
    
            return _metadata;
        }
    
        function _removeBatchFromOwner(address from, Batch memory batch) private {
            // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
    
            uint256 globalIndex = getBatchIndex(batch.startTokenId);
    
            uint256 lastBatchIndex = batchesOwned[from].length - 1;
            uint256 batchIndex = ownedBatchIndex[globalIndex];
    
            // When the token to delete is the last token, the swap operation is unnecessary
            if (batchIndex != lastBatchIndex) {
                Batch memory lastBatch = batchesOwned[from][lastBatchIndex];
                uint256 lastGlobalIndex = getBatchIndex(lastBatch.startTokenId);
    
                batchesOwned[from][batchIndex] = lastBatch; // Move the last batch to the slot of the to-delete batch
                ownedBatchIndex[lastGlobalIndex] = batchIndex; // Update the moved batch's index
            }
    
            // This also deletes the contents at the last position of the array
            batchesOwned[from].length--;
    
            // Note that ownedBatchIndex[batch] hasn't been cleared: it still points to the old slot (now occupied by
            // lastBatch, or just over the end of the array if the batch was the last one).
        }
    
        function _addBatchToOwner(address to, Batch memory batch) private {
            uint256 globalIndex = getBatchIndex(batch.startTokenId);
    
            ownedBatchIndex[globalIndex] = batchesOwned[to].length;
            batchesOwned[to].push(batch);
        }
    
        function batchTransfer(uint256 batchIndex, address to) public {
            Batch storage a = allBatches[batchIndex];
    
            address previousOwner = a.owner;
    
            require(a.owner == msg.sender, "You don't own this batch");
    
            _removeBatchFromOwner(previousOwner, a);
    
            a.owner = to;
    
            _addBatchToOwner(to, a);
    
            emit BatchTransfer(previousOwner, to, batchIndex);
    
            //Now to need to emit a bunch of transfer events
            uint end = a.startTokenId + a.size;
            //uint256 unActivated = 0;
            uint256 tokensMoved = 0;
            for (uint i = a.startTokenId; i < end; i++) {
                if (outOfBatch[i]) {
                    continue;
                }
                tokensMoved++;
                emit Transfer(previousOwner, to, i);
            }
    
            _ownedTokensCount[to] += tokensMoved;
            _ownedTokensCount[previousOwner] -= tokensMoved;
        }
    
        /**
         * @dev Gets the token name.
         * @return string representing the token name
         */
        function name() external pure returns (string memory) {
            return _name;
        }
    
        /**
         * @dev Gets the token symbol.
         * @return string representing the token symbol
         */
        function symbol() external pure returns (string memory) {
            return _symbol;
        }
    
        /**
         * @dev Gets the list of token IDs of the requested owner.
         * @param __owner address owning the tokens
         * @return uint256[] List of token IDs owned by the requested address
         */
        function _tokensOfOwner(address __owner) internal view returns (uint256[] storage) {
            return _ownedTokens[__owner];
        }
    
        /**
         * @dev Private function to add a token to this extension's ownership-tracking data structures.
         * @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 _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
            _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
            _ownedTokens[to].push(tokenId);
        }
    
        /**
         * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
         * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
         * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
         * This has O(1) time complexity, but alters the order of the _ownedTokens array.
         * @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 _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) internal {
            // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
    
            uint256 lastTokenIndex = _ownedTokens[from].length - 1;
            uint256 tokenIndex = _ownedTokensIndex[tokenId];
    
            // When the token to delete is the last token, the swap operation is unnecessary
            if (tokenIndex != lastTokenIndex) {
                uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
    
                _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
            }
    
            // This also deletes the contents at the last position of the array
            _ownedTokens[from].length--;
    
            // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
            // lastTokenId, or just over the end of the array if the token was the last one).
        }
    
        /**
         * @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), "ERC721: balance query for the zero address");
    
            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 address currently marked as the owner of the given token ID
         */
        function ownerOf(uint256 tokenId) public view returns (address) {
            if (outOfBatch[tokenId]) {
                address __owner = _tokenOwner[tokenId];
                require(__owner != address(0), "ERC721: owner query for nonexistent token");
    
                return __owner;
            }
    
            uint256 index = getBatchIndex(tokenId);
            require(index < allBatches.length, "Token batch doesn't exist");
            Batch memory a = allBatches[index];
            require(tokenId < a.startTokenId + a.size, "Token outside bounds of batch");
            return a.owner;
        }
    
        /**
         * @dev Approves another address to transfer the given token ID
         * The zero address indicates there is no approved address.
         * There can only be one approved address per token at a given time.
         * 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, "ERC721: approval to current owner");
    
            require(_msgSender() == __owner || isApprovedForAll(__owner, _msgSender()),
                "ERC721: approve caller is not owner nor approved for all"
            );
    
            _tokenApprovals[tokenId] = to;
            emit Approval(__owner, to, tokenId);
        }
    
        /**
         * @dev Gets the approved address for a token ID, or zero if no address set
         * Reverts if the token ID does not exist.
         * @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) {
            require(exists(tokenId), "ERC721: approved query for nonexistent token");
    
            return _tokenApprovals[tokenId];
        }
    
        /**
         * @dev Sets or unsets the approval of a given operator
         * 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 != _msgSender(), "ERC721: approve to caller");
    
            _operatorApprovals[_msgSender()][to] = approved;
            emit ApprovalForAll(_msgSender(), 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.
         * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         * 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 {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    
            _transferFrom(from, to, tokenId);
        }
    
        /**
         * @dev Safely transfers the ownership of a given token ID to another address
         * If the target address is a contract, it must implement {IERC721Receiver-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.
         * 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 {
            safeTransferFrom(from, to, tokenId, "");
        }
    
        /**
         * @dev Safely transfers the ownership of a given token ID to another address
         * If the target address is a contract, it must implement {IERC721Receiver-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.
         * Requires the _msgSender() 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 memory _data) public {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _safeTransferFrom(from, to, tokenId, _data);
        }
    
        /**
         * @dev Safely transfers the ownership of a given token ID to another address
         * 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.
         * 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 memory _data) internal {
            _transferFrom(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
    
        /**
         * @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) {
            require(exists(tokenId), "ERC721: operator query for nonexistent token");
            address __owner = ownerOf(tokenId);
            return (spender == __owner || getApproved(tokenId) == spender || isApprovedForAll(__owner, spender));
        }
    
        /**
         * @dev Internal function to mint a new token.
         * 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
         */
        function _addTokenTo(address to, uint256 tokenId) internal {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!exists(tokenId), "ERC721: token already minted");
    
            _tokenOwner[tokenId] = to;
            _ownedTokensCount[to]++;
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    
        /**
         * @dev Internal function to transfer ownership of a given token ID to another address.
         * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         * @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) internal {
            require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
            require(to != address(0), "ERC721: transfer to the zero address");
    
            uint256 index = getBatchIndex(tokenId);
            require(index < allBatches.length, "Token batch doesn't exist");
            Batch memory a = allBatches[index];
            require(tokenId < a.startTokenId + a.size, "Token out of bounds in batch");
    
            bool shouldRemove = false;
            bool shouldAdd = false;
    
            if (!outOfBatch[tokenId]) {
                //If this tokenID hasn't left it's origin batch
                //mark it as so
                outOfBatch[tokenId] = true;
    
                //Add token to 'to' address
                //(This will also increase their balance)
                //_addTokenTo(to, tokenId);
                shouldAdd = true;
            } else {
                if (to == a.owner) {
                    //If this token is going back to the batch owner
                    //Then mark it as inside the batch
                    outOfBatch[tokenId] = false;
    
                    //And remove the token from 'from' address
                    //_removeTokenFromOwnerEnumeration(from, tokenId);
                    shouldRemove = true;
                } else {
                    shouldRemove = true;
                    shouldAdd = true;
                }
            }
    
            _clearApproval(tokenId);
    
            _ownedTokensCount[from]--;
            _ownedTokensCount[to]++;
    
            _tokenOwner[tokenId] = to;
    
            if (shouldRemove) {
                _removeTokenFromOwnerEnumeration(from, tokenId);
            }
    
            if (shouldAdd) {
                _addTokenToOwnerEnumeration(to, tokenId);
            }
    
            emit Transfer(from, to, tokenId);
        }
    
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * This function is deprecated.
         * @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 bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
            internal returns (bool)
        {
            if (!to.isContract()) {
                return true;
            }
    
            bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data);
            return (retval == _ERC721_RECEIVED);
        }
    
        /**
         * @dev Private function to clear current approval of a given token ID.
         * @param tokenId uint256 ID of the token to be transferred
         */
        function _clearApproval(uint256 tokenId) private {
            if (_tokenApprovals[tokenId] != address(0)) {
                _tokenApprovals[tokenId] = address(0);
            }
        }
    
        /**
        * Returns the number of tokens that are owned outside of a batch for a given owner
        */
        function fallbackCount(address __owner) public view returns (uint256) {
            return _ownedTokens[__owner].length;
        }
    
        /**
        * Returns a token at a given index that are owned outside of a batch for a given owner
        */
        function fallbackIndex(address __owner, uint256 index) public view returns (uint256) {
            return _ownedTokens[__owner][index];
        }
        
        function gunCountForCategory(uint8 category) public view returns (uint256) {
            uint256 count = 0;
            for (uint256 i = 0; i < allBatches.length; i++) {
                Batch memory b = allBatches[i];
                if (b.category == category) {
                    count += b.size;
                }
            }
    
            return count;
        }
    
        function updateGunFactory(address new_factory) external onlyOwner {
            factory = new_factory;
        }
    }