ETH Price: $2,173.79 (+1.36%)

Transaction Decoder

Block:
15780502 at Oct-19-2022 06:56:11 AM +UTC
Transaction Fee:
0.00547049618199535 ETH $11.89
Gas Used:
341,275 Gas / 16.029583714 Gwei

Emitted Events:

168 ALittleSimmon.Approval( owner=0xb20f7676a119d3586cd23f11138c8b25a40119c8, approved=0x0000000000000000000000000000000000000000, tokenId=749 )
169 ALittleSimmon.Transfer( from=0xb20f7676a119d3586cd23f11138c8b25a40119c8, to=[Sender] 0x4b4a0721fb34579c7c18aa6a41a449492392af74, tokenId=749 )
170 0x191b0e61ccbf3b26df76ca4321d9dee4c960e91e.0xc4109843e0b7d514e4c093114b863f8e7d8d9a458c372cd51bfe526b588006c9( 0xc4109843e0b7d514e4c093114b863f8e7d8d9a458c372cd51bfe526b588006c9, 0x000000000000000000000000b20f7676a119d3586cd23f11138c8b25a40119c8, 0x00000000000000000000000092701d42e1504ef9fce6d66a2054218b048dda43, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 254acb4a74f9c30e48055973d1d120b3d4823201c67c0e6fd189c356ff6a731f, 000000000000000000000000000000000000000000000000002386f26fc10000 )
171 OwnedUpgradeabilityProxy.0xa43f4bcf06b7e28c335ab6096cae3215c7ca0cedc6f3063a067950e5c2b49211( 0xa43f4bcf06b7e28c335ab6096cae3215c7ca0cedc6f3063a067950e5c2b49211, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000000000000000000000000000000000000000000001, 254acb4a74f9c30e48055973d1d120b3d4823201c67c0e6fd189c356ff6a731f, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001 )

Account State Difference:

  Address   Before After State Difference Code
0x191b0e61...4C960E91E
0x4b4a0721...92392aF74
0.036824873295991896 Eth
Nonce: 26
0.021354377113996546 Eth
Nonce: 27
0.01547049618199535
(builder0x69)
0.885135696315660049 Eth0.885647608815660049 Eth0.0005119125
0x8D098e7e...34d6f945d
0xB20F7676...5a40119C8 0.00101075114683459 Eth0.01101075114683459 Eth0.01

Execution Trace

ETH 0.01 OwnedUpgradeabilityProxy.25046071( )
  • ETH 0.01 0xa12a1ebc7627b24fc008daa62f672f9784c980ce.25046071( )
    • 0x219d1fc3bad3df628c49350696a31281d392e6e8.b1283e77( )
    • ETH 0.01 OKX NFT 4.ab834bab( )
      • ETH 0.01 0x29a1da843083dd2972c419f654124316278b374c.ab834bab( )
        • OwnedUpgradeabilityProxy.46423aa7( )
          • 0x12daaca2a15ce2580ad071ab386f7f2cccac7e49.46423aa7( )
          • Null: 0x000...001.254acb4a( )
          • 0x3b61dbab91cf90e74d2411361acb463ac6838483.c4552791( )
          • 0x75182ae161049ea564ae5b0d7e92327b404b3930.f4f635fa( )
            • 0x0e561f6c386d3ea0de5311bd083cb170cfee9bbd.2782d6c7( )
            • ALittleSimmon.supportsInterface( interfaceId=System.Byte[] ) => ( False )
            • Null: 0x000...000.CALL( )
            • ETH 0.01 0xb20f7676a119d3586cd23f11138c8b25a40119c8.CALL( )
            • 0x8d65ccff285007240f5c947ff93faa622414b2f0.2692f25a( )
              • ALittleSimmon.safeTransferFrom( from=0xB20F7676a119D3586Cd23f11138C8B25a40119C8, to=0x4b4a0721Fb34579c7C18AA6A41a449492392aF74, tokenId=749 )
                File 1 of 3: OwnedUpgradeabilityProxy
                pragma solidity ^0.5.0;
                
                
                /**
                 * @title Proxy
                 * @dev Gives the possibility to delegate any call to a foreign implementation.
                 */
                contract Proxy {
                    /**
                    * @dev Fallback function allowing to perform a delegatecall to the given implementation.
                    * This function will return whatever the implementation call returns
                    */
                    function () external payable {
                        address _impl = implementation();
                        require(_impl != address(0));
                
                        assembly {
                            let ptr := mload(0x40)
                            calldatacopy(ptr, 0, calldatasize)
                            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                            let size := returndatasize
                            returndatacopy(ptr, 0, size)
                
                            switch result
                            case 0 { revert(ptr, size) }
                            default { return(ptr, size) }
                        }
                    }
                
                    /**
                    * @dev Tells the address of the implementation where every call will be delegated.
                    * @return address of the implementation to which it will be delegated
                    */
                    function implementation() public view returns (address);
                }
                
                /**
                 * @title UpgradeabilityProxy
                 * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
                 */
                contract UpgradeabilityProxy is Proxy {
                    /**
                    * @dev This event will be emitted every time the implementation gets upgraded
                    * @param implementation representing the address of the upgraded implementation
                    */
                    event Upgraded(address indexed implementation);
                
                    // Storage position of the address of the current implementation
                    bytes32 private constant IMPLEMENTATION_POSITION = keccak256("offchain.proxy.implementation");
                
                    /**
                    * @dev Constructor function
                    */
                    constructor() public {}
                
                    /**
                    * @dev Tells the address of the current implementation
                    * @return address of the current implementation
                    */
                    function implementation() public view returns (address impl) {
                        bytes32 position = IMPLEMENTATION_POSITION;
                        assembly {
                            impl := sload(position)
                        }
                    }
                
                    /**
                    * @dev Sets the address of the current implementation
                    * @param _newImplementation address representing the new implementation to be set
                    */
                    function _setImplementation(address _newImplementation) internal {
                        bytes32 position = IMPLEMENTATION_POSITION;
                        assembly {
                            sstore(position, _newImplementation)
                        }
                    }
                
                    /**
                    * @dev Upgrades the implementation address
                    * @param _newImplementation representing the address of the new implementation to be set
                    */
                    function _upgradeTo(address _newImplementation) internal {
                        address currentImplementation = implementation();
                        require(currentImplementation != _newImplementation);
                        _setImplementation(_newImplementation);
                        emit Upgraded(_newImplementation);
                    }
                }
                
                /**
                 * @title OwnedUpgradeabilityProxy
                 * @dev This contract combines an upgradeability proxy with basic authorization control functionalities
                 */
                contract OwnedUpgradeabilityProxy is UpgradeabilityProxy {
                    /**
                    * @dev Event to show ownership has been transferred
                    * @param previousOwner representing the address of the previous owner
                    * @param newOwner representing the address of the new owner
                    */
                    event ProxyOwnershipTransferred(address previousOwner, address newOwner);
                
                    // Storage position of the owner of the contract
                    bytes32 private constant PROXY_OWNER_POSITION = keccak256("offchain.proxy.owner");
                
                    /**
                    * @dev the constructor sets the original owner of the contract to the sender account.
                    */
                    constructor(address _implementation) public {
                        _setUpgradeabilityOwner(msg.sender);
                        _upgradeTo(_implementation);
                    }
                
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyProxyOwner() {
                        require(msg.sender == proxyOwner());
                        _;
                    }
                
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function proxyOwner() public view returns (address owner) {
                        bytes32 position = PROXY_OWNER_POSITION;
                        assembly {
                            owner := sload(position)
                        }
                    }
                
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param _newOwner The address to transfer ownership to.
                    */
                    function transferProxyOwnership(address _newOwner) public onlyProxyOwner {
                        require(_newOwner != address(0));
                        _setUpgradeabilityOwner(_newOwner);
                        emit ProxyOwnershipTransferred(proxyOwner(), _newOwner);
                    }
                
                    /**
                    * @dev Allows the proxy owner to upgrade the current version of the proxy.
                    * @param _implementation representing the address of the new implementation to be set.
                    */
                    function upgradeTo(address _implementation) public onlyProxyOwner {
                        _upgradeTo(_implementation);
                    }
                
                    /**
                     * @dev Sets the address of the owner
                    */
                    function _setUpgradeabilityOwner(address _newProxyOwner) internal {
                        bytes32 position = PROXY_OWNER_POSITION;
                        assembly {
                            sstore(position, _newProxyOwner)
                        }
                    }
                }

                File 2 of 3: ALittleSimmon
                // SPDX-License-Identifier: MIT
                pragma solidity >=0.8.9 <0.9.0;
                import 'erc721a/contracts/extensions/ERC721AQueryable.sol';
                import '@openzeppelin/contracts/access/Ownable.sol';
                import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
                import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
                contract ALittleSimmon is ERC721AQueryable, Ownable, ReentrancyGuard {
                  using Strings for uint256;
                  bytes32 public merkleRoot;
                  mapping(address => bool) public whitelistClaimed;
                  string public uriPrefix = '';
                  string public uriSuffix = '.json';
                  string public hiddenMetadataUri;
                  
                  uint256 public cost;
                  uint256 public maxSupply;
                  uint256 public maxMintAmountPerTx;
                  bool public paused = true;
                  bool public whitelistMintEnabled = false;
                  bool public revealed = false;
                  constructor(
                    string memory _tokenName,
                    string memory _tokenSymbol,
                    uint256 _cost,
                    uint256 _maxSupply,
                    uint256 _maxMintAmountPerTx,
                    string memory _hiddenMetadataUri
                  ) ERC721A(_tokenName, _tokenSymbol) {
                    setCost(_cost);
                    maxSupply = _maxSupply;
                    setMaxMintAmountPerTx(_maxMintAmountPerTx);
                    setHiddenMetadataUri(_hiddenMetadataUri);
                  }
                  modifier mintCompliance(uint256 _mintAmount) {
                    require(_mintAmount > 0 && _mintAmount <= maxMintAmountPerTx, 'Invalid mint amount!');
                    require(totalSupply() + _mintAmount <= maxSupply, 'Max supply exceeded!');
                    _;
                  }
                  modifier mintPriceCompliance(uint256 _mintAmount) {
                    require(msg.value >= cost * _mintAmount, 'Insufficient funds!');
                    _;
                  }
                  function whitelistMint(uint256 _mintAmount, bytes32[] calldata _merkleProof) public payable mintCompliance(_mintAmount) mintPriceCompliance(_mintAmount) {
                    // Verify whitelist requirements
                    require(whitelistMintEnabled, 'The whitelist sale is not enabled!');
                    require(!whitelistClaimed[_msgSender()], 'Address already claimed!');
                    bytes32 leaf = keccak256(abi.encodePacked(_msgSender()));
                    require(MerkleProof.verify(_merkleProof, merkleRoot, leaf), 'Invalid proof!');
                    whitelistClaimed[_msgSender()] = true;
                    _safeMint(_msgSender(), _mintAmount);
                  }
                  function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) mintPriceCompliance(_mintAmount) {
                    require(!paused, 'The contract is paused!');
                    _safeMint(_msgSender(), _mintAmount);
                  }
                  
                  function mintForAddress(uint256 _mintAmount, address _receiver) public mintCompliance(_mintAmount) onlyOwner {
                    _safeMint(_receiver, _mintAmount);
                  }
                  function _startTokenId() internal view virtual override returns (uint256) {
                    return 1;
                  }
                  function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
                    require(_exists(_tokenId), 'ERC721Metadata: URI query for nonexistent token');
                    if (revealed == false) {
                      return hiddenMetadataUri;
                    }
                    string memory currentBaseURI = _baseURI();
                    return bytes(currentBaseURI).length > 0
                        ? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), uriSuffix))
                        : '';
                  }
                  function setRevealed(bool _state) public onlyOwner {
                    revealed = _state;
                  }
                  function setCost(uint256 _cost) public onlyOwner {
                    cost = _cost;
                  }
                  function setMaxMintAmountPerTx(uint256 _maxMintAmountPerTx) public onlyOwner {
                    maxMintAmountPerTx = _maxMintAmountPerTx;
                  }
                  function setHiddenMetadataUri(string memory _hiddenMetadataUri) public onlyOwner {
                    hiddenMetadataUri = _hiddenMetadataUri;
                  }
                  function setUriPrefix(string memory _uriPrefix) public onlyOwner {
                    uriPrefix = _uriPrefix;
                  }
                  function setUriSuffix(string memory _uriSuffix) public onlyOwner {
                    uriSuffix = _uriSuffix;
                  }
                  function setPaused(bool _state) public onlyOwner {
                    paused = _state;
                  }
                  function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner {
                    merkleRoot = _merkleRoot;
                  }
                  function setWhitelistMintEnabled(bool _state) public onlyOwner {
                    whitelistMintEnabled = _state;
                  }
                  function withdraw() public onlyOwner nonReentrant {
                    // This will transfer the remaining contract balance to the owner.
                    // Do not remove this otherwise you will not be able to withdraw the funds.
                    // =============================================================================
                    (bool os, ) = payable(owner()).call{value: address(this).balance}('');
                    require(os);
                    // =============================================================================
                  }
                  function _baseURI() internal view virtual override returns (string memory) {
                    return uriPrefix;
                  }
                }
                // SPDX-License-Identifier: MIT
                // ERC721A Contracts v3.3.0
                // Creator: Chiru Labs
                pragma solidity ^0.8.4;
                import './IERC721AQueryable.sol';
                import '../ERC721A.sol';
                /**
                 * @title ERC721A Queryable
                 * @dev ERC721A subclass with convenience query functions.
                 */
                abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable {
                    /**
                     * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
                     *
                     * If the `tokenId` is out of bounds:
                     *   - `addr` = `address(0)`
                     *   - `startTimestamp` = `0`
                     *   - `burned` = `false`
                     *
                     * If the `tokenId` is burned:
                     *   - `addr` = `<Address of owner before token was burned>`
                     *   - `startTimestamp` = `<Timestamp when token was burned>`
                     *   - `burned = `true`
                     *
                     * Otherwise:
                     *   - `addr` = `<Address of owner>`
                     *   - `startTimestamp` = `<Timestamp of start of ownership>`
                     *   - `burned = `false`
                     */
                    function explicitOwnershipOf(uint256 tokenId) public view override returns (TokenOwnership memory) {
                        TokenOwnership memory ownership;
                        if (tokenId < _startTokenId() || tokenId >= _currentIndex) {
                            return ownership;
                        }
                        ownership = _ownerships[tokenId];
                        if (ownership.burned) {
                            return ownership;
                        }
                        return _ownershipOf(tokenId);
                    }
                    /**
                     * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
                     * See {ERC721AQueryable-explicitOwnershipOf}
                     */
                    function explicitOwnershipsOf(uint256[] memory tokenIds) external view override returns (TokenOwnership[] memory) {
                        unchecked {
                            uint256 tokenIdsLength = tokenIds.length;
                            TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                            for (uint256 i; i != tokenIdsLength; ++i) {
                                ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                            }
                            return ownerships;
                        }
                    }
                    /**
                     * @dev Returns an array of token IDs owned by `owner`,
                     * in the range [`start`, `stop`)
                     * (i.e. `start <= tokenId < stop`).
                     *
                     * This function allows for tokens to be queried if the collection
                     * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
                     *
                     * Requirements:
                     *
                     * - `start` < `stop`
                     */
                    function tokensOfOwnerIn(
                        address owner,
                        uint256 start,
                        uint256 stop
                    ) external view override returns (uint256[] memory) {
                        unchecked {
                            if (start >= stop) revert InvalidQueryRange();
                            uint256 tokenIdsIdx;
                            uint256 stopLimit = _currentIndex;
                            // Set `start = max(start, _startTokenId())`.
                            if (start < _startTokenId()) {
                                start = _startTokenId();
                            }
                            // Set `stop = min(stop, _currentIndex)`.
                            if (stop > stopLimit) {
                                stop = stopLimit;
                            }
                            uint256 tokenIdsMaxLength = balanceOf(owner);
                            // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                            // to cater for cases where `balanceOf(owner)` is too big.
                            if (start < stop) {
                                uint256 rangeLength = stop - start;
                                if (rangeLength < tokenIdsMaxLength) {
                                    tokenIdsMaxLength = rangeLength;
                                }
                            } else {
                                tokenIdsMaxLength = 0;
                            }
                            uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                            if (tokenIdsMaxLength == 0) {
                                return tokenIds;
                            }
                            // We need to call `explicitOwnershipOf(start)`,
                            // because the slot at `start` may not be initialized.
                            TokenOwnership memory ownership = explicitOwnershipOf(start);
                            address currOwnershipAddr;
                            // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
                            // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
                            if (!ownership.burned) {
                                currOwnershipAddr = ownership.addr;
                            }
                            for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                                ownership = _ownerships[i];
                                if (ownership.burned) {
                                    continue;
                                }
                                if (ownership.addr != address(0)) {
                                    currOwnershipAddr = ownership.addr;
                                }
                                if (currOwnershipAddr == owner) {
                                    tokenIds[tokenIdsIdx++] = i;
                                }
                            }
                            // Downsize the array to fit.
                            assembly {
                                mstore(tokenIds, tokenIdsIdx)
                            }
                            return tokenIds;
                        }
                    }
                    /**
                     * @dev Returns an array of token IDs owned by `owner`.
                     *
                     * This function scans the ownership mapping and is O(totalSupply) in complexity.
                     * It is meant to be called off-chain.
                     *
                     * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
                     * multiple smaller scans if the collection is large enough to cause
                     * an out-of-gas error (10K pfp collections should be fine).
                     */
                    function tokensOfOwner(address owner) external view override returns (uint256[] memory) {
                        unchecked {
                            uint256 tokenIdsIdx;
                            address currOwnershipAddr;
                            uint256 tokenIdsLength = balanceOf(owner);
                            uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                            TokenOwnership memory ownership;
                            for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                                ownership = _ownerships[i];
                                if (ownership.burned) {
                                    continue;
                                }
                                if (ownership.addr != address(0)) {
                                    currOwnershipAddr = ownership.addr;
                                }
                                if (currOwnershipAddr == owner) {
                                    tokenIds[tokenIdsIdx++] = i;
                                }
                            }
                            return tokenIds;
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
                pragma solidity ^0.8.0;
                import "../utils/Context.sol";
                /**
                 * @dev Contract module which provides a basic access control mechanism, where
                 * there is an account (an owner) that can be granted exclusive access to
                 * specific functions.
                 *
                 * By default, the owner account will be the one that deploys the contract. This
                 * can later be changed with {transferOwnership}.
                 *
                 * This module is used through inheritance. It will make available the modifier
                 * `onlyOwner`, which can be applied to your functions to restrict their use to
                 * the owner.
                 */
                abstract contract Ownable is Context {
                    address private _owner;
                    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    /**
                     * @dev Initializes the contract setting the deployer as the initial owner.
                     */
                    constructor() {
                        _transferOwnership(_msgSender());
                    }
                    /**
                     * @dev Returns the address of the current owner.
                     */
                    function owner() public view virtual returns (address) {
                        return _owner;
                    }
                    /**
                     * @dev Throws if called by any account other than the owner.
                     */
                    modifier onlyOwner() {
                        require(owner() == _msgSender(), "Ownable: caller is not the 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 virtual onlyOwner {
                        _transferOwnership(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 virtual onlyOwner {
                        require(newOwner != address(0), "Ownable: new owner is the zero address");
                        _transferOwnership(newOwner);
                    }
                    /**
                     * @dev Transfers ownership of the contract to a new account (`newOwner`).
                     * Internal function without access restriction.
                     */
                    function _transferOwnership(address newOwner) internal virtual {
                        address oldOwner = _owner;
                        _owner = newOwner;
                        emit OwnershipTransferred(oldOwner, newOwner);
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev These functions deal with verification of Merkle Trees proofs.
                 *
                 * The proofs can be generated using the JavaScript library
                 * https://github.com/miguelmota/merkletreejs[merkletreejs].
                 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
                 *
                 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
                 */
                library MerkleProof {
                    /**
                     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                     * defined by `root`. For this, a `proof` must be provided, containing
                     * sibling hashes on the branch from the leaf to the root of the tree. Each
                     * pair of leaves and each pair of pre-images are assumed to be sorted.
                     */
                    function verify(
                        bytes32[] memory proof,
                        bytes32 root,
                        bytes32 leaf
                    ) internal pure returns (bool) {
                        return processProof(proof, leaf) == root;
                    }
                    /**
                     * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
                     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
                     * hash matches the root of the tree. When processing the proof, the pairs
                     * of leafs & pre-images are assumed to be sorted.
                     *
                     * _Available since v4.4._
                     */
                    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                        bytes32 computedHash = leaf;
                        for (uint256 i = 0; i < proof.length; i++) {
                            bytes32 proofElement = proof[i];
                            if (computedHash <= proofElement) {
                                // Hash(current computed hash + current element of the proof)
                                computedHash = _efficientHash(computedHash, proofElement);
                            } else {
                                // Hash(current element of the proof + current computed hash)
                                computedHash = _efficientHash(proofElement, computedHash);
                            }
                        }
                        return computedHash;
                    }
                    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                        assembly {
                            mstore(0x00, a)
                            mstore(0x20, b)
                            value := keccak256(0x00, 0x40)
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Contract module that helps prevent reentrant calls to a function.
                 *
                 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                 * available, which can be applied to functions to make sure there are no nested
                 * (reentrant) calls to them.
                 *
                 * Note that because there is a single `nonReentrant` guard, functions marked as
                 * `nonReentrant` may not call one another. This can be worked around by making
                 * those functions `private`, and then adding `external` `nonReentrant` entry
                 * points to them.
                 *
                 * TIP: If you would like to learn more about reentrancy and alternative ways
                 * to protect against it, check out our blog post
                 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                 */
                abstract contract ReentrancyGuard {
                    // Booleans are more expensive than uint256 or any type that takes up a full
                    // word because each write operation emits an extra SLOAD to first read the
                    // slot's contents, replace the bits taken up by the boolean, and then write
                    // back. This is the compiler's defense against contract upgrades and
                    // pointer aliasing, and it cannot be disabled.
                    // The values being non-zero value makes deployment a bit more expensive,
                    // but in exchange the refund on every call to nonReentrant will be lower in
                    // amount. Since refunds are capped to a percentage of the total
                    // transaction's gas, it is best to keep them low in cases like this one, to
                    // increase the likelihood of the full refund coming into effect.
                    uint256 private constant _NOT_ENTERED = 1;
                    uint256 private constant _ENTERED = 2;
                    uint256 private _status;
                    constructor() {
                        _status = _NOT_ENTERED;
                    }
                    /**
                     * @dev Prevents a contract from calling itself, directly or indirectly.
                     * Calling a `nonReentrant` function from another `nonReentrant`
                     * function is not supported. It is possible to prevent this from happening
                     * by making the `nonReentrant` function external, and making it call a
                     * `private` function that does the actual work.
                     */
                    modifier nonReentrant() {
                        // On the first call to nonReentrant, _notEntered will be true
                        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                        // Any calls to nonReentrant after this point will fail
                        _status = _ENTERED;
                        _;
                        // By storing the original value once again, a refund is triggered (see
                        // https://eips.ethereum.org/EIPS/eip-2200)
                        _status = _NOT_ENTERED;
                    }
                }
                // SPDX-License-Identifier: MIT
                // ERC721A Contracts v3.3.0
                // Creator: Chiru Labs
                pragma solidity ^0.8.4;
                import '../IERC721A.sol';
                /**
                 * @dev Interface of an ERC721AQueryable compliant contract.
                 */
                interface IERC721AQueryable is IERC721A {
                    /**
                     * Invalid query range (`start` >= `stop`).
                     */
                    error InvalidQueryRange();
                    /**
                     * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
                     *
                     * If the `tokenId` is out of bounds:
                     *   - `addr` = `address(0)`
                     *   - `startTimestamp` = `0`
                     *   - `burned` = `false`
                     *
                     * If the `tokenId` is burned:
                     *   - `addr` = `<Address of owner before token was burned>`
                     *   - `startTimestamp` = `<Timestamp when token was burned>`
                     *   - `burned = `true`
                     *
                     * Otherwise:
                     *   - `addr` = `<Address of owner>`
                     *   - `startTimestamp` = `<Timestamp of start of ownership>`
                     *   - `burned = `false`
                     */
                    function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
                    /**
                     * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
                     * See {ERC721AQueryable-explicitOwnershipOf}
                     */
                    function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
                    /**
                     * @dev Returns an array of token IDs owned by `owner`,
                     * in the range [`start`, `stop`)
                     * (i.e. `start <= tokenId < stop`).
                     *
                     * This function allows for tokens to be queried if the collection
                     * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
                     *
                     * Requirements:
                     *
                     * - `start` < `stop`
                     */
                    function tokensOfOwnerIn(
                        address owner,
                        uint256 start,
                        uint256 stop
                    ) external view returns (uint256[] memory);
                    /**
                     * @dev Returns an array of token IDs owned by `owner`.
                     *
                     * This function scans the ownership mapping and is O(totalSupply) in complexity.
                     * It is meant to be called off-chain.
                     *
                     * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
                     * multiple smaller scans if the collection is large enough to cause
                     * an out-of-gas error (10K pfp collections should be fine).
                     */
                    function tokensOfOwner(address owner) external view returns (uint256[] memory);
                }
                // SPDX-License-Identifier: MIT
                // ERC721A Contracts v3.3.0
                // Creator: Chiru Labs
                pragma solidity ^0.8.4;
                import './IERC721A.sol';
                import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
                import '@openzeppelin/contracts/utils/Address.sol';
                import '@openzeppelin/contracts/utils/Context.sol';
                import '@openzeppelin/contracts/utils/Strings.sol';
                import '@openzeppelin/contracts/utils/introspection/ERC165.sol';
                /**
                 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
                 * the Metadata extension. Built to optimize for lower gas during batch mints.
                 *
                 * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
                 *
                 * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
                 *
                 * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
                 */
                contract ERC721A is Context, ERC165, IERC721A {
                    using Address for address;
                    using Strings for uint256;
                    // The tokenId of the next token to be minted.
                    uint256 internal _currentIndex;
                    // The number of tokens burned.
                    uint256 internal _burnCounter;
                    // Token name
                    string private _name;
                    // Token symbol
                    string private _symbol;
                    // Mapping from token ID to ownership details
                    // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
                    mapping(uint256 => TokenOwnership) internal _ownerships;
                    // Mapping owner address to address data
                    mapping(address => AddressData) private _addressData;
                    // Mapping from token ID to approved address
                    mapping(uint256 => address) private _tokenApprovals;
                    // Mapping from owner to operator approvals
                    mapping(address => mapping(address => bool)) private _operatorApprovals;
                    constructor(string memory name_, string memory symbol_) {
                        _name = name_;
                        _symbol = symbol_;
                        _currentIndex = _startTokenId();
                    }
                    /**
                     * To change the starting tokenId, please override this function.
                     */
                    function _startTokenId() internal view virtual returns (uint256) {
                        return 0;
                    }
                    /**
                     * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
                     */
                    function totalSupply() public view override returns (uint256) {
                        // Counter underflow is impossible as _burnCounter cannot be incremented
                        // more than _currentIndex - _startTokenId() times
                        unchecked {
                            return _currentIndex - _burnCounter - _startTokenId();
                        }
                    }
                    /**
                     * Returns the total amount of tokens minted in the contract.
                     */
                    function _totalMinted() internal view returns (uint256) {
                        // Counter underflow is impossible as _currentIndex does not decrement,
                        // and it is initialized to _startTokenId()
                        unchecked {
                            return _currentIndex - _startTokenId();
                        }
                    }
                    /**
                     * @dev See {IERC165-supportsInterface}.
                     */
                    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                        return
                            interfaceId == type(IERC721).interfaceId ||
                            interfaceId == type(IERC721Metadata).interfaceId ||
                            super.supportsInterface(interfaceId);
                    }
                    /**
                     * @dev See {IERC721-balanceOf}.
                     */
                    function balanceOf(address owner) public view override returns (uint256) {
                        if (owner == address(0)) revert BalanceQueryForZeroAddress();
                        return uint256(_addressData[owner].balance);
                    }
                    /**
                     * Returns the number of tokens minted by `owner`.
                     */
                    function _numberMinted(address owner) internal view returns (uint256) {
                        return uint256(_addressData[owner].numberMinted);
                    }
                    /**
                     * Returns the number of tokens burned by or on behalf of `owner`.
                     */
                    function _numberBurned(address owner) internal view returns (uint256) {
                        return uint256(_addressData[owner].numberBurned);
                    }
                    /**
                     * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                     */
                    function _getAux(address owner) internal view returns (uint64) {
                        return _addressData[owner].aux;
                    }
                    /**
                     * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                     * If there are multiple variables, please pack them into a uint64.
                     */
                    function _setAux(address owner, uint64 aux) internal {
                        _addressData[owner].aux = aux;
                    }
                    /**
                     * Gas spent here starts off proportional to the maximum mint batch size.
                     * It gradually moves to O(1) as tokens get transferred around in the collection over time.
                     */
                    function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
                        uint256 curr = tokenId;
                        unchecked {
                            if (_startTokenId() <= curr) if (curr < _currentIndex) {
                                TokenOwnership memory ownership = _ownerships[curr];
                                if (!ownership.burned) {
                                    if (ownership.addr != address(0)) {
                                        return ownership;
                                    }
                                    // Invariant:
                                    // There will always be an ownership that has an address and is not burned
                                    // before an ownership that does not have an address and is not burned.
                                    // Hence, curr will not underflow.
                                    while (true) {
                                        curr--;
                                        ownership = _ownerships[curr];
                                        if (ownership.addr != address(0)) {
                                            return ownership;
                                        }
                                    }
                                }
                            }
                        }
                        revert OwnerQueryForNonexistentToken();
                    }
                    /**
                     * @dev See {IERC721-ownerOf}.
                     */
                    function ownerOf(uint256 tokenId) public view override returns (address) {
                        return _ownershipOf(tokenId).addr;
                    }
                    /**
                     * @dev See {IERC721Metadata-name}.
                     */
                    function name() public view virtual override returns (string memory) {
                        return _name;
                    }
                    /**
                     * @dev See {IERC721Metadata-symbol}.
                     */
                    function symbol() public view virtual override returns (string memory) {
                        return _symbol;
                    }
                    /**
                     * @dev See {IERC721Metadata-tokenURI}.
                     */
                    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                        string memory baseURI = _baseURI();
                        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : '';
                    }
                    /**
                     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                     * by default, can be overriden in child contracts.
                     */
                    function _baseURI() internal view virtual returns (string memory) {
                        return '';
                    }
                    /**
                     * @dev See {IERC721-approve}.
                     */
                    function approve(address to, uint256 tokenId) public override {
                        address owner = ERC721A.ownerOf(tokenId);
                        if (to == owner) revert ApprovalToCurrentOwner();
                        if (_msgSender() != owner) if(!isApprovedForAll(owner, _msgSender())) {
                            revert ApprovalCallerNotOwnerNorApproved();
                        }
                        _approve(to, tokenId, owner);
                    }
                    /**
                     * @dev See {IERC721-getApproved}.
                     */
                    function getApproved(uint256 tokenId) public view override returns (address) {
                        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                        return _tokenApprovals[tokenId];
                    }
                    /**
                     * @dev See {IERC721-setApprovalForAll}.
                     */
                    function setApprovalForAll(address operator, bool approved) public virtual override {
                        if (operator == _msgSender()) revert ApproveToCaller();
                        _operatorApprovals[_msgSender()][operator] = approved;
                        emit ApprovalForAll(_msgSender(), operator, approved);
                    }
                    /**
                     * @dev See {IERC721-isApprovedForAll}.
                     */
                    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                        return _operatorApprovals[owner][operator];
                    }
                    /**
                     * @dev See {IERC721-transferFrom}.
                     */
                    function transferFrom(
                        address from,
                        address to,
                        uint256 tokenId
                    ) public virtual override {
                        _transfer(from, to, tokenId);
                    }
                    /**
                     * @dev See {IERC721-safeTransferFrom}.
                     */
                    function safeTransferFrom(
                        address from,
                        address to,
                        uint256 tokenId
                    ) public virtual override {
                        safeTransferFrom(from, to, tokenId, '');
                    }
                    /**
                     * @dev See {IERC721-safeTransferFrom}.
                     */
                    function safeTransferFrom(
                        address from,
                        address to,
                        uint256 tokenId,
                        bytes memory _data
                    ) public virtual override {
                        _transfer(from, to, tokenId);
                        if (to.isContract()) if(!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                            revert TransferToNonERC721ReceiverImplementer();
                        }
                    }
                    /**
                     * @dev Returns whether `tokenId` exists.
                     *
                     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                     *
                     * Tokens start existing when they are minted (`_mint`),
                     */
                    function _exists(uint256 tokenId) internal view returns (bool) {
                        return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
                    }
                    /**
                     * @dev Equivalent to `_safeMint(to, quantity, '')`.
                     */
                    function _safeMint(address to, uint256 quantity) internal {
                        _safeMint(to, quantity, '');
                    }
                    /**
                     * @dev Safely mints `quantity` tokens and transfers them to `to`.
                     *
                     * Requirements:
                     *
                     * - If `to` refers to a smart contract, it must implement
                     *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
                     * - `quantity` must be greater than 0.
                     *
                     * Emits a {Transfer} event.
                     */
                    function _safeMint(
                        address to,
                        uint256 quantity,
                        bytes memory _data
                    ) internal {
                        uint256 startTokenId = _currentIndex;
                        if (to == address(0)) revert MintToZeroAddress();
                        if (quantity == 0) revert MintZeroQuantity();
                        _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                        // Overflows are incredibly unrealistic.
                        // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                        // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                        unchecked {
                            _addressData[to].balance += uint64(quantity);
                            _addressData[to].numberMinted += uint64(quantity);
                            _ownerships[startTokenId].addr = to;
                            _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                            uint256 updatedIndex = startTokenId;
                            uint256 end = updatedIndex + quantity;
                            if (to.isContract()) {
                                do {
                                    emit Transfer(address(0), to, updatedIndex);
                                    if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) {
                                        revert TransferToNonERC721ReceiverImplementer();
                                    }
                                } while (updatedIndex < end);
                                // Reentrancy protection
                                if (_currentIndex != startTokenId) revert();
                            } else {
                                do {
                                    emit Transfer(address(0), to, updatedIndex++);
                                } while (updatedIndex < end);
                            }
                            _currentIndex = updatedIndex;
                        }
                        _afterTokenTransfers(address(0), to, startTokenId, quantity);
                    }
                    /**
                     * @dev Mints `quantity` tokens and transfers them to `to`.
                     *
                     * Requirements:
                     *
                     * - `to` cannot be the zero address.
                     * - `quantity` must be greater than 0.
                     *
                     * Emits a {Transfer} event.
                     */
                    function _mint(address to, uint256 quantity) internal {
                        uint256 startTokenId = _currentIndex;
                        if (to == address(0)) revert MintToZeroAddress();
                        if (quantity == 0) revert MintZeroQuantity();
                        _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                        // Overflows are incredibly unrealistic.
                        // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                        // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                        unchecked {
                            _addressData[to].balance += uint64(quantity);
                            _addressData[to].numberMinted += uint64(quantity);
                            _ownerships[startTokenId].addr = to;
                            _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                            uint256 updatedIndex = startTokenId;
                            uint256 end = updatedIndex + quantity;
                            do {
                                emit Transfer(address(0), to, updatedIndex++);
                            } while (updatedIndex < end);
                            _currentIndex = updatedIndex;
                        }
                        _afterTokenTransfers(address(0), to, startTokenId, quantity);
                    }
                    /**
                     * @dev Transfers `tokenId` from `from` to `to`.
                     *
                     * Requirements:
                     *
                     * - `to` cannot be the zero address.
                     * - `tokenId` token must be owned by `from`.
                     *
                     * Emits a {Transfer} event.
                     */
                    function _transfer(
                        address from,
                        address to,
                        uint256 tokenId
                    ) private {
                        TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                        if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
                        bool isApprovedOrOwner = (_msgSender() == from ||
                            isApprovedForAll(from, _msgSender()) ||
                            getApproved(tokenId) == _msgSender());
                        if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                        if (to == address(0)) revert TransferToZeroAddress();
                        _beforeTokenTransfers(from, to, tokenId, 1);
                        // Clear approvals from the previous owner
                        _approve(address(0), tokenId, from);
                        // Underflow of the sender's balance is impossible because we check for
                        // ownership above and the recipient's balance can't realistically overflow.
                        // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                        unchecked {
                            _addressData[from].balance -= 1;
                            _addressData[to].balance += 1;
                            TokenOwnership storage currSlot = _ownerships[tokenId];
                            currSlot.addr = to;
                            currSlot.startTimestamp = uint64(block.timestamp);
                            // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
                            // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                            uint256 nextTokenId = tokenId + 1;
                            TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                            if (nextSlot.addr == address(0)) {
                                // This will suffice for checking _exists(nextTokenId),
                                // as a burned slot cannot contain the zero address.
                                if (nextTokenId != _currentIndex) {
                                    nextSlot.addr = from;
                                    nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                }
                            }
                        }
                        emit Transfer(from, to, tokenId);
                        _afterTokenTransfers(from, to, tokenId, 1);
                    }
                    /**
                     * @dev Equivalent to `_burn(tokenId, false)`.
                     */
                    function _burn(uint256 tokenId) internal virtual {
                        _burn(tokenId, false);
                    }
                    /**
                     * @dev Destroys `tokenId`.
                     * The approval is cleared when the token is burned.
                     *
                     * Requirements:
                     *
                     * - `tokenId` must exist.
                     *
                     * Emits a {Transfer} event.
                     */
                    function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                        TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                        address from = prevOwnership.addr;
                        if (approvalCheck) {
                            bool isApprovedOrOwner = (_msgSender() == from ||
                                isApprovedForAll(from, _msgSender()) ||
                                getApproved(tokenId) == _msgSender());
                            if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                        }
                        _beforeTokenTransfers(from, address(0), tokenId, 1);
                        // Clear approvals from the previous owner
                        _approve(address(0), tokenId, from);
                        // Underflow of the sender's balance is impossible because we check for
                        // ownership above and the recipient's balance can't realistically overflow.
                        // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                        unchecked {
                            AddressData storage addressData = _addressData[from];
                            addressData.balance -= 1;
                            addressData.numberBurned += 1;
                            // Keep track of who burned the token, and the timestamp of burning.
                            TokenOwnership storage currSlot = _ownerships[tokenId];
                            currSlot.addr = from;
                            currSlot.startTimestamp = uint64(block.timestamp);
                            currSlot.burned = true;
                            // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
                            // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                            uint256 nextTokenId = tokenId + 1;
                            TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                            if (nextSlot.addr == address(0)) {
                                // This will suffice for checking _exists(nextTokenId),
                                // as a burned slot cannot contain the zero address.
                                if (nextTokenId != _currentIndex) {
                                    nextSlot.addr = from;
                                    nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                }
                            }
                        }
                        emit Transfer(from, address(0), tokenId);
                        _afterTokenTransfers(from, address(0), tokenId, 1);
                        // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                        unchecked {
                            _burnCounter++;
                        }
                    }
                    /**
                     * @dev Approve `to` to operate on `tokenId`
                     *
                     * Emits a {Approval} event.
                     */
                    function _approve(
                        address to,
                        uint256 tokenId,
                        address owner
                    ) private {
                        _tokenApprovals[tokenId] = to;
                        emit Approval(owner, to, tokenId);
                    }
                    /**
                     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target 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 bool whether the call correctly returned the expected magic value
                     */
                    function _checkContractOnERC721Received(
                        address from,
                        address to,
                        uint256 tokenId,
                        bytes memory _data
                    ) private returns (bool) {
                        try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                            return retval == IERC721Receiver(to).onERC721Received.selector;
                        } catch (bytes memory reason) {
                            if (reason.length == 0) {
                                revert TransferToNonERC721ReceiverImplementer();
                            } else {
                                assembly {
                                    revert(add(32, reason), mload(reason))
                                }
                            }
                        }
                    }
                    /**
                     * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
                     * And also called before burning one token.
                     *
                     * startTokenId - the first token id to be transferred
                     * quantity - the amount to be transferred
                     *
                     * Calling conditions:
                     *
                     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
                     * transferred to `to`.
                     * - When `from` is zero, `tokenId` will be minted for `to`.
                     * - When `to` is zero, `tokenId` will be burned by `from`.
                     * - `from` and `to` are never both zero.
                     */
                    function _beforeTokenTransfers(
                        address from,
                        address to,
                        uint256 startTokenId,
                        uint256 quantity
                    ) internal virtual {}
                    /**
                     * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
                     * minting.
                     * And also called after one token has been burned.
                     *
                     * startTokenId - the first token id to be transferred
                     * quantity - the amount to be transferred
                     *
                     * Calling conditions:
                     *
                     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                     * transferred to `to`.
                     * - When `from` is zero, `tokenId` has been minted for `to`.
                     * - When `to` is zero, `tokenId` has been burned by `from`.
                     * - `from` and `to` are never both zero.
                     */
                    function _afterTokenTransfers(
                        address from,
                        address to,
                        uint256 startTokenId,
                        uint256 quantity
                    ) internal virtual {}
                }
                // SPDX-License-Identifier: MIT
                // ERC721A Contracts v3.3.0
                // Creator: Chiru Labs
                pragma solidity ^0.8.4;
                import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
                import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
                /**
                 * @dev Interface of an ERC721A compliant contract.
                 */
                interface IERC721A is IERC721, IERC721Metadata {
                    /**
                     * The caller must own the token or be an approved operator.
                     */
                    error ApprovalCallerNotOwnerNorApproved();
                    /**
                     * The token does not exist.
                     */
                    error ApprovalQueryForNonexistentToken();
                    /**
                     * The caller cannot approve to their own address.
                     */
                    error ApproveToCaller();
                    /**
                     * The caller cannot approve to the current owner.
                     */
                    error ApprovalToCurrentOwner();
                    /**
                     * Cannot query the balance for the zero address.
                     */
                    error BalanceQueryForZeroAddress();
                    /**
                     * Cannot mint to the zero address.
                     */
                    error MintToZeroAddress();
                    /**
                     * The quantity of tokens minted must be more than zero.
                     */
                    error MintZeroQuantity();
                    /**
                     * The token does not exist.
                     */
                    error OwnerQueryForNonexistentToken();
                    /**
                     * The caller must own the token or be an approved operator.
                     */
                    error TransferCallerNotOwnerNorApproved();
                    /**
                     * The token must be owned by `from`.
                     */
                    error TransferFromIncorrectOwner();
                    /**
                     * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
                     */
                    error TransferToNonERC721ReceiverImplementer();
                    /**
                     * Cannot transfer to the zero address.
                     */
                    error TransferToZeroAddress();
                    /**
                     * The token does not exist.
                     */
                    error URIQueryForNonexistentToken();
                    // Compiler will pack this into a single 256bit word.
                    struct TokenOwnership {
                        // The address of the owner.
                        address addr;
                        // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                        uint64 startTimestamp;
                        // Whether the token has been burned.
                        bool burned;
                    }
                    // Compiler will pack this into a single 256bit word.
                    struct AddressData {
                        // Realistically, 2**64-1 is more than enough.
                        uint64 balance;
                        // Keeps track of mint count with minimal overhead for tokenomics.
                        uint64 numberMinted;
                        // Keeps track of burn count with minimal overhead for tokenomics.
                        uint64 numberBurned;
                        // For miscellaneous variable(s) pertaining to the address
                        // (e.g. number of whitelist mint slots used).
                        // If there are multiple variables, please pack them into a uint64.
                        uint64 aux;
                    }
                    /**
                     * @dev Returns the total amount of tokens stored by the contract.
                     * 
                     * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
                     */
                    function totalSupply() external view returns (uint256);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
                pragma solidity ^0.8.0;
                import "../../utils/introspection/IERC165.sol";
                /**
                 * @dev Required interface of an ERC721 compliant contract.
                 */
                interface IERC721 is IERC165 {
                    /**
                     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                    /**
                     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                     */
                    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                    /**
                     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                     */
                    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                    /**
                     * @dev Returns the number of tokens in ``owner``'s account.
                     */
                    function balanceOf(address owner) external view returns (uint256 balance);
                    /**
                     * @dev Returns the owner of the `tokenId` token.
                     *
                     * Requirements:
                     *
                     * - `tokenId` must exist.
                     */
                    function ownerOf(uint256 tokenId) external view returns (address owner);
                    /**
                     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                     *
                     * Requirements:
                     *
                     * - `from` cannot be the zero address.
                     * - `to` cannot be the zero address.
                     * - `tokenId` token must exist and be owned by `from`.
                     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                     *
                     * Emits a {Transfer} event.
                     */
                    function safeTransferFrom(
                        address from,
                        address to,
                        uint256 tokenId
                    ) external;
                    /**
                     * @dev Transfers `tokenId` token from `from` to `to`.
                     *
                     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                     *
                     * Requirements:
                     *
                     * - `from` cannot be the zero address.
                     * - `to` cannot be the zero address.
                     * - `tokenId` token must be owned by `from`.
                     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(
                        address from,
                        address to,
                        uint256 tokenId
                    ) external;
                    /**
                     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                     * The approval is cleared when the token is transferred.
                     *
                     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                     *
                     * Requirements:
                     *
                     * - The caller must own the token or be an approved operator.
                     * - `tokenId` must exist.
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address to, uint256 tokenId) external;
                    /**
                     * @dev Returns the account approved for `tokenId` token.
                     *
                     * Requirements:
                     *
                     * - `tokenId` must exist.
                     */
                    function getApproved(uint256 tokenId) external view returns (address operator);
                    /**
                     * @dev Approve or remove `operator` as an operator for the caller.
                     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                     *
                     * Requirements:
                     *
                     * - The `operator` cannot be the caller.
                     *
                     * Emits an {ApprovalForAll} event.
                     */
                    function setApprovalForAll(address operator, bool _approved) external;
                    /**
                     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                     *
                     * See {setApprovalForAll}
                     */
                    function isApprovedForAll(address owner, address operator) external view returns (bool);
                    /**
                     * @dev Safely transfers `tokenId` token from `from` to `to`.
                     *
                     * Requirements:
                     *
                     * - `from` cannot be the zero address.
                     * - `to` cannot be the zero address.
                     * - `tokenId` token must exist and be owned by `from`.
                     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                     *
                     * Emits a {Transfer} event.
                     */
                    function safeTransferFrom(
                        address from,
                        address to,
                        uint256 tokenId,
                        bytes calldata data
                    ) external;
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
                pragma solidity ^0.8.0;
                import "../IERC721.sol";
                /**
                 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                 * @dev See https://eips.ethereum.org/EIPS/eip-721
                 */
                interface IERC721Metadata is IERC721 {
                    /**
                     * @dev Returns the token collection name.
                     */
                    function name() external view returns (string memory);
                    /**
                     * @dev Returns the token collection symbol.
                     */
                    function symbol() external view returns (string memory);
                    /**
                     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                     */
                    function tokenURI(uint256 tokenId) external view returns (string memory);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
                pragma solidity ^0.8.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);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
                pragma solidity ^0.8.0;
                /**
                 * @title ERC721 token receiver interface
                 * @dev Interface for any contract that wants to support safeTransfers
                 * from ERC721 asset contracts.
                 */
                interface IERC721Receiver {
                    /**
                     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                     * by `operator` from `from`, this function is called.
                     *
                     * It must return its Solidity selector to confirm the token transfer.
                     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                     *
                     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                     */
                    function onERC721Received(
                        address operator,
                        address from,
                        uint256 tokenId,
                        bytes calldata data
                    ) external returns (bytes4);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
                pragma solidity ^0.8.1;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is 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.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     *
                     * [IMPORTANT]
                     * ====
                     * You shouldn't rely on `isContract` to protect against flash loan attacks!
                     *
                     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                     * constructor.
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize/address.code.length, which returns 0
                        // for contracts in construction, since the code is only stored at the end
                        // of the constructor execution.
                        return account.code.length > 0;
                    }
                    /**
                     * @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].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        require(isContract(target), "Address: call to non-contract");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        require(isContract(target), "Address: static call to non-contract");
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(isContract(target), "Address: delegate call to non-contract");
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            // Look for revert reason and bubble it up if present
                            if (returndata.length > 0) {
                                // The easiest way to bubble the revert reason is using memory via assembly
                                assembly {
                                    let returndata_size := mload(returndata)
                                    revert(add(32, returndata), returndata_size)
                                }
                            } else {
                                revert(errorMessage);
                            }
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                pragma solidity ^0.8.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 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.
                 */
                abstract contract Context {
                    function _msgSender() internal view virtual returns (address) {
                        return msg.sender;
                    }
                    function _msgData() internal view virtual returns (bytes calldata) {
                        return msg.data;
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev String operations.
                 */
                library Strings {
                    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                    /**
                     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                     */
                    function toString(uint256 value) internal pure returns (string memory) {
                        // Inspired by OraclizeAPI's implementation - MIT licence
                        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                        if (value == 0) {
                            return "0";
                        }
                        uint256 temp = value;
                        uint256 digits;
                        while (temp != 0) {
                            digits++;
                            temp /= 10;
                        }
                        bytes memory buffer = new bytes(digits);
                        while (value != 0) {
                            digits -= 1;
                            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                            value /= 10;
                        }
                        return string(buffer);
                    }
                    /**
                     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                     */
                    function toHexString(uint256 value) internal pure returns (string memory) {
                        if (value == 0) {
                            return "0x00";
                        }
                        uint256 temp = value;
                        uint256 length = 0;
                        while (temp != 0) {
                            length++;
                            temp >>= 8;
                        }
                        return toHexString(value, length);
                    }
                    /**
                     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                     */
                    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                        bytes memory buffer = new bytes(2 * length + 2);
                        buffer[0] = "0";
                        buffer[1] = "x";
                        for (uint256 i = 2 * length + 1; i > 1; --i) {
                            buffer[i] = _HEX_SYMBOLS[value & 0xf];
                            value >>= 4;
                        }
                        require(value == 0, "Strings: hex length insufficient");
                        return string(buffer);
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                pragma solidity ^0.8.0;
                import "./IERC165.sol";
                /**
                 * @dev Implementation of the {IERC165} interface.
                 *
                 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                 * for the additional interface id that will be supported. For example:
                 *
                 * ```solidity
                 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                 * }
                 * ```
                 *
                 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
                 */
                abstract contract ERC165 is IERC165 {
                    /**
                     * @dev See {IERC165-supportsInterface}.
                     */
                    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                        return interfaceId == type(IERC165).interfaceId;
                    }
                }
                

                File 3 of 3: OwnedUpgradeabilityProxy
                pragma solidity ^0.5.0;
                
                
                /**
                 * @title Proxy
                 * @dev Gives the possibility to delegate any call to a foreign implementation.
                 */
                contract Proxy {
                    /**
                    * @dev Fallback function allowing to perform a delegatecall to the given implementation.
                    * This function will return whatever the implementation call returns
                    */
                    function () external payable {
                        address _impl = implementation();
                        require(_impl != address(0));
                
                        assembly {
                            let ptr := mload(0x40)
                            calldatacopy(ptr, 0, calldatasize)
                            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                            let size := returndatasize
                            returndatacopy(ptr, 0, size)
                
                            switch result
                            case 0 { revert(ptr, size) }
                            default { return(ptr, size) }
                        }
                    }
                
                    /**
                    * @dev Tells the address of the implementation where every call will be delegated.
                    * @return address of the implementation to which it will be delegated
                    */
                    function implementation() public view returns (address);
                }
                
                /**
                 * @title UpgradeabilityProxy
                 * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
                 */
                contract UpgradeabilityProxy is Proxy {
                    /**
                    * @dev This event will be emitted every time the implementation gets upgraded
                    * @param implementation representing the address of the upgraded implementation
                    */
                    event Upgraded(address indexed implementation);
                
                    // Storage position of the address of the current implementation
                    bytes32 private constant IMPLEMENTATION_POSITION = keccak256("offchain.proxy.implementation");
                
                    /**
                    * @dev Constructor function
                    */
                    constructor() public {}
                
                    /**
                    * @dev Tells the address of the current implementation
                    * @return address of the current implementation
                    */
                    function implementation() public view returns (address impl) {
                        bytes32 position = IMPLEMENTATION_POSITION;
                        assembly {
                            impl := sload(position)
                        }
                    }
                
                    /**
                    * @dev Sets the address of the current implementation
                    * @param _newImplementation address representing the new implementation to be set
                    */
                    function _setImplementation(address _newImplementation) internal {
                        bytes32 position = IMPLEMENTATION_POSITION;
                        assembly {
                            sstore(position, _newImplementation)
                        }
                    }
                
                    /**
                    * @dev Upgrades the implementation address
                    * @param _newImplementation representing the address of the new implementation to be set
                    */
                    function _upgradeTo(address _newImplementation) internal {
                        address currentImplementation = implementation();
                        require(currentImplementation != _newImplementation);
                        _setImplementation(_newImplementation);
                        emit Upgraded(_newImplementation);
                    }
                }
                
                /**
                 * @title OwnedUpgradeabilityProxy
                 * @dev This contract combines an upgradeability proxy with basic authorization control functionalities
                 */
                contract OwnedUpgradeabilityProxy is UpgradeabilityProxy {
                    /**
                    * @dev Event to show ownership has been transferred
                    * @param previousOwner representing the address of the previous owner
                    * @param newOwner representing the address of the new owner
                    */
                    event ProxyOwnershipTransferred(address previousOwner, address newOwner);
                
                    // Storage position of the owner of the contract
                    bytes32 private constant PROXY_OWNER_POSITION = keccak256("offchain.proxy.owner");
                
                    /**
                    * @dev the constructor sets the original owner of the contract to the sender account.
                    */
                    constructor(address _implementation) public {
                        _setUpgradeabilityOwner(msg.sender);
                        _upgradeTo(_implementation);
                    }
                
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyProxyOwner() {
                        require(msg.sender == proxyOwner());
                        _;
                    }
                
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function proxyOwner() public view returns (address owner) {
                        bytes32 position = PROXY_OWNER_POSITION;
                        assembly {
                            owner := sload(position)
                        }
                    }
                
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param _newOwner The address to transfer ownership to.
                    */
                    function transferProxyOwnership(address _newOwner) public onlyProxyOwner {
                        require(_newOwner != address(0));
                        _setUpgradeabilityOwner(_newOwner);
                        emit ProxyOwnershipTransferred(proxyOwner(), _newOwner);
                    }
                
                    /**
                    * @dev Allows the proxy owner to upgrade the current version of the proxy.
                    * @param _implementation representing the address of the new implementation to be set.
                    */
                    function upgradeTo(address _implementation) public onlyProxyOwner {
                        _upgradeTo(_implementation);
                    }
                
                    /**
                     * @dev Sets the address of the owner
                    */
                    function _setUpgradeabilityOwner(address _newProxyOwner) internal {
                        bytes32 position = PROXY_OWNER_POSITION;
                        assembly {
                            sstore(position, _newProxyOwner)
                        }
                    }
                }